examples: remove old examples and update README (new benches/examples will be added).
This commit is contained in:
parent
5247a2008a
commit
53057121e7
16
README.md
16
README.md
|
@ -60,20 +60,8 @@ enjoy-digital.fr.
|
||||||
[> Getting started
|
[> Getting started
|
||||||
------------------
|
------------------
|
||||||
1. Install Python 3.6+ and FPGA vendor's development tools.
|
1. Install Python 3.6+ and FPGA vendor's development tools.
|
||||||
2. Install Migen/LiteX and the LiteX's cores:
|
2. Install LiteX and the cores by following the LiteX's wiki [installation guide](https://github.com/enjoy-digital/litex/wiki/Installation).
|
||||||
|
3. You can find examples of integration of the core with LiteX in LiteX-Boards and in the examples directory.
|
||||||
```sh
|
|
||||||
$ wget https://raw.githubusercontent.com/enjoy-digital/litex/master/litex_setup.py
|
|
||||||
$ chmod +x litex_setup.py
|
|
||||||
$ ./litex_setup.py init install --user (--user to install to user directory)
|
|
||||||
```
|
|
||||||
Later, if you need to update all repositories:
|
|
||||||
```sh
|
|
||||||
$ ./litex_setup.py update
|
|
||||||
```
|
|
||||||
|
|
||||||
3. Check out /examples/versa_ecp5_udp_loopback for a good practical example of how to get
|
|
||||||
started with the Liteeth core solo in an FPGA.
|
|
||||||
|
|
||||||
[> Tests
|
[> Tests
|
||||||
--------
|
--------
|
||||||
|
|
159
examples/make.py
159
examples/make.py
|
@ -1,159 +0,0 @@
|
||||||
#!/usr/bin/env python3
|
|
||||||
|
|
||||||
#
|
|
||||||
# This file is part of LiteEth.
|
|
||||||
#
|
|
||||||
# Copyright (c) 2015-2018 Florent Kermarrec <florent@enjoy-digital.fr>
|
|
||||||
# SPDX-License-Identifier: BSD-2-Clause
|
|
||||||
|
|
||||||
import sys
|
|
||||||
import os
|
|
||||||
import argparse
|
|
||||||
import subprocess
|
|
||||||
import struct
|
|
||||||
import importlib
|
|
||||||
|
|
||||||
from migen.fhdl import verilog
|
|
||||||
from migen.fhdl.structure import _Fragment
|
|
||||||
|
|
||||||
from litex.build.tools import write_to_file
|
|
||||||
from litex.build.xilinx.common import *
|
|
||||||
|
|
||||||
from litex.soc.integration import export
|
|
||||||
|
|
||||||
liteeth_path = "../"
|
|
||||||
sys.path.append(liteeth_path) # XXX
|
|
||||||
from liteeth.common import *
|
|
||||||
|
|
||||||
|
|
||||||
def autotype(s):
|
|
||||||
if s == "True":
|
|
||||||
return True
|
|
||||||
elif s == "False":
|
|
||||||
return False
|
|
||||||
try:
|
|
||||||
return int(s, 0)
|
|
||||||
except ValueError:
|
|
||||||
pass
|
|
||||||
return s
|
|
||||||
|
|
||||||
|
|
||||||
def _import(default, name):
|
|
||||||
return importlib.import_module(default + "." + name)
|
|
||||||
|
|
||||||
|
|
||||||
def _get_args():
|
|
||||||
parser = argparse.ArgumentParser(formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
||||||
description="""\
|
|
||||||
LiteEth - based on Migen.
|
|
||||||
|
|
||||||
This program builds and/or loads LiteEth components.
|
|
||||||
One or several actions can be specified:
|
|
||||||
|
|
||||||
clean delete previous build(s).
|
|
||||||
build-rtl build verilog rtl.
|
|
||||||
build-bitstream build-bitstream build FPGA bitstream.
|
|
||||||
build-csr-csv save CSR map into CSV file.
|
|
||||||
|
|
||||||
load-bitstream load bitstream into volatile storage.
|
|
||||||
|
|
||||||
all clean, build-csr-csv, build-bitstream, load-bitstream.
|
|
||||||
""")
|
|
||||||
|
|
||||||
parser.add_argument("-t", "--target", default="base", help="Core type to build")
|
|
||||||
parser.add_argument("-s", "--sub-target", default="", help="variant of the Core type to build")
|
|
||||||
parser.add_argument("-p", "--platform", default=None, help="platform to build for")
|
|
||||||
parser.add_argument("-Ot", "--target-option", default=[], nargs=2, action="append", help="set target-specific option")
|
|
||||||
parser.add_argument("-Op", "--platform-option", default=[], nargs=2, action="append", help="set platform-specific option")
|
|
||||||
parser.add_argument("-Ob", "--build-option", default=[], nargs=2, action="append", help="set build option")
|
|
||||||
parser.add_argument("--csr_csv", default="./test/csr.csv", help="CSV file to save the CSR map into")
|
|
||||||
|
|
||||||
parser.add_argument("action", nargs="+", help="specify an action")
|
|
||||||
|
|
||||||
return parser.parse_args()
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
args = _get_args()
|
|
||||||
|
|
||||||
# create top-level Core object
|
|
||||||
target_module = _import("targets", args.target)
|
|
||||||
if args.sub_target:
|
|
||||||
top_class = getattr(target_module, args.sub_target)
|
|
||||||
else:
|
|
||||||
top_class = target_module.default_subtarget
|
|
||||||
|
|
||||||
if args.platform is None:
|
|
||||||
if hasattr(top_class, "default_platform"):
|
|
||||||
platform_name = top_class.default_platform
|
|
||||||
else:
|
|
||||||
raise ValueError("Target has no default platform, specify a platform with -p your_platform")
|
|
||||||
else:
|
|
||||||
platform_name = args.platform
|
|
||||||
platform_module = _import("litex.boards.platforms", platform_name)
|
|
||||||
platform_kwargs = dict((k, autotype(v)) for k, v in args.platform_option)
|
|
||||||
platform = platform_module.Platform(**platform_kwargs)
|
|
||||||
|
|
||||||
build_name = top_class.__name__.lower() + "_" + platform_name
|
|
||||||
top_kwargs = dict((k, autotype(v)) for k, v in args.target_option)
|
|
||||||
soc = top_class(platform, **top_kwargs)
|
|
||||||
soc.finalize()
|
|
||||||
|
|
||||||
# decode actions
|
|
||||||
action_list = ["clean", "build-csr-csv", "build-bitstream", "load-bitstream", "all"]
|
|
||||||
actions = {k: False for k in action_list}
|
|
||||||
for action in args.action:
|
|
||||||
if action in actions:
|
|
||||||
actions[action] = True
|
|
||||||
else:
|
|
||||||
print("Unknown action: "+action+". Valid actions are:")
|
|
||||||
for a in action_list:
|
|
||||||
print(" "+a)
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
print("""
|
|
||||||
__ _ __ ______ __
|
|
||||||
/ / (_) /____ / __/ /_/ /
|
|
||||||
/ /__/ / __/ -_) _// __/ _ \\
|
|
||||||
/____/_/\__/\__/___/\__/_//_/
|
|
||||||
|
|
||||||
A small footprint and configurable Ethernet
|
|
||||||
core powered by Migen & LiteX
|
|
||||||
====== Building options: ======
|
|
||||||
Platform: {}
|
|
||||||
Target: {}
|
|
||||||
Subtarget: {}
|
|
||||||
System Clk: {} MHz
|
|
||||||
===============================""".format(
|
|
||||||
platform_name,
|
|
||||||
args.target,
|
|
||||||
top_class.__name__,
|
|
||||||
soc.clk_freq/1000000
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
# dependencies
|
|
||||||
if actions["all"]:
|
|
||||||
actions["build-csr-csv"] = True
|
|
||||||
actions["build-bitstream"] = True
|
|
||||||
actions["load-bitstream"] = True
|
|
||||||
|
|
||||||
if actions["build-bitstream"]:
|
|
||||||
actions["build-csr-csv"] = True
|
|
||||||
|
|
||||||
if actions["clean"]:
|
|
||||||
subprocess.call(["rm", "-rf", "build/*"])
|
|
||||||
|
|
||||||
if actions["build-csr-csv"]:
|
|
||||||
csr_csv = export.get_csr_csv(soc.csr_regions)
|
|
||||||
write_to_file(args.csr_csv, csr_csv)
|
|
||||||
|
|
||||||
if actions["build-bitstream"]:
|
|
||||||
build_kwargs = dict((k, autotype(v)) for k, v in args.build_option)
|
|
||||||
vns = platform.build(soc, build_name=build_name, **build_kwargs)
|
|
||||||
if hasattr(soc, "do_exit") and vns is not None:
|
|
||||||
if hasattr(soc.do_exit, '__call__'):
|
|
||||||
soc.do_exit(vns)
|
|
||||||
|
|
||||||
if actions["load-bitstream"]:
|
|
||||||
prog = platform.create_programmer()
|
|
||||||
prog.load_bitstream("build/" + build_name + platform.bitstream_ext)
|
|
|
@ -1,118 +0,0 @@
|
||||||
#
|
|
||||||
# This file is part of LiteEth.
|
|
||||||
#
|
|
||||||
# Copyright (c) 2015-2019 Florent Kermarrec <florent@enjoy-digital.fr>
|
|
||||||
# SPDX-License-Identifier: BSD-2-Clause
|
|
||||||
|
|
||||||
from litex.build.io import CRG
|
|
||||||
from litex.build.xilinx.vivado import XilinxVivadoToolchain
|
|
||||||
from litex.soc.interconnect import wishbone
|
|
||||||
|
|
||||||
from litex.soc.integration.soc_core import SoCCore
|
|
||||||
from litex.soc.cores.uart import UARTWishboneBridge
|
|
||||||
|
|
||||||
from liteeth.common import *
|
|
||||||
from liteeth.phy import LiteEthPHY
|
|
||||||
from liteeth.core import LiteEthUDPIPCore
|
|
||||||
|
|
||||||
# BaseSoC ------------------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
class BaseSoC(SoCCore):
|
|
||||||
def __init__(self, platform, clk_freq=int(166e6),
|
|
||||||
mac_address = 0x10e2d5000000,
|
|
||||||
ip_address = "192.168.1.50"):
|
|
||||||
sys_clk_freq = int((1/(platform.default_clk_period))*1e9)
|
|
||||||
SoCCore.__init__(self, platform, clk_freq,
|
|
||||||
cpu_type = None,
|
|
||||||
csr_data_width = 32,
|
|
||||||
with_uart = False,
|
|
||||||
ident = "LiteEth Base Design",
|
|
||||||
with_timer = False
|
|
||||||
)
|
|
||||||
|
|
||||||
# Serial Wishbone Bridge
|
|
||||||
serial_bridge = UARTWishboneBridge(platform.request("serial"), sys_clk_freq, baudrate=115200)
|
|
||||||
self.submodules += serial_bridge
|
|
||||||
self.add_wb_master(serial_bridge.wishbone)
|
|
||||||
self.submodules.crg = CRG(platform.request(platform.default_clk_name))
|
|
||||||
|
|
||||||
# Ethernet PHY and UDP/IP stack
|
|
||||||
self.submodules.ethphy = ethphy = LiteEthPHY(
|
|
||||||
clock_pads = platform.request("eth_clocks"),
|
|
||||||
pads = platform.request("eth"),
|
|
||||||
clk_freq = clk_freq)
|
|
||||||
self.add_csr("ethphy")
|
|
||||||
self.submodules.ethcore = ethcore = LiteEthUDPIPCore(
|
|
||||||
phy = ethphy,
|
|
||||||
mac_address = mac_address,
|
|
||||||
ip_address = ip_address,
|
|
||||||
clk_freq = clk_freq)
|
|
||||||
self.add_csr("ethcore")
|
|
||||||
|
|
||||||
if isinstance(platform.toolchain, XilinxVivadoToolchain):
|
|
||||||
self.crg.cd_sys.clk.attr.add("keep")
|
|
||||||
ethphy.crg.cd_eth_rx.clk.attr.add("keep")
|
|
||||||
ethphy.crg.cd_eth_tx.clk.attr.add("keep")
|
|
||||||
platform.add_period_constraint(self.ethphy.crg.cd_eth_rx.clk, 1e9/125e6)
|
|
||||||
platform.add_period_constraint(self.ethphy.crg.cd_eth_tx.clk, 1e9/125e6)
|
|
||||||
platform.add_false_path_constraints(
|
|
||||||
self.crg.cd_sys.clk,
|
|
||||||
ethphy.crg.cd_eth_rx.clk,
|
|
||||||
ethphy.crg.cd_eth_tx.clk)
|
|
||||||
|
|
||||||
# BaseSoCDevel -------------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
class BaseSoCDevel(BaseSoC):
|
|
||||||
def __init__(self, platform):
|
|
||||||
from litescope import LiteScopeAnalyzer
|
|
||||||
BaseSoC.__init__(self, platform)
|
|
||||||
|
|
||||||
analyzer_signals = [
|
|
||||||
# MAC interface
|
|
||||||
self.ethcore.mac.core.sink.valid,
|
|
||||||
self.ethcore.mac.core.sink.last,
|
|
||||||
self.ethcore.mac.core.sink.ready,
|
|
||||||
self.ethcore.mac.core.sink.data,
|
|
||||||
|
|
||||||
self.ethcore.mac.core.source.valid,
|
|
||||||
self.ethcore.mac.core.source.last,
|
|
||||||
self.ethcore.mac.core.source.ready,
|
|
||||||
self.ethcore.mac.core.source.data,
|
|
||||||
|
|
||||||
# ICMP interface
|
|
||||||
self.ethcore.icmp.echo.sink.valid,
|
|
||||||
self.ethcore.icmp.echo.sink.last,
|
|
||||||
self.ethcore.icmp.echo.sink.ready,
|
|
||||||
self.ethcore.icmp.echo.sink.data,
|
|
||||||
|
|
||||||
self.ethcore.icmp.echo.source.valid,
|
|
||||||
self.ethcore.icmp.echo.source.last,
|
|
||||||
self.ethcore.icmp.echo.source.ready,
|
|
||||||
self.ethcore.icmp.echo.source.data,
|
|
||||||
|
|
||||||
# IP interface
|
|
||||||
self.ethcore.ip.crossbar.master.sink.valid,
|
|
||||||
self.ethcore.ip.crossbar.master.sink.last,
|
|
||||||
self.ethcore.ip.crossbar.master.sink.ready,
|
|
||||||
self.ethcore.ip.crossbar.master.sink.data,
|
|
||||||
self.ethcore.ip.crossbar.master.sink.ip_address,
|
|
||||||
self.ethcore.ip.crossbar.master.sink.protocol,
|
|
||||||
|
|
||||||
# State machines
|
|
||||||
self.ethcore.icmp.rx.fsm,
|
|
||||||
self.ethcore.icmp.tx.fsm,
|
|
||||||
|
|
||||||
self.ethcore.arp.rx.fsm,
|
|
||||||
self.ethcore.arp.tx.fsm,
|
|
||||||
self.ethcore.arp.table.fsm,
|
|
||||||
|
|
||||||
self.ethcore.ip.rx.fsm,
|
|
||||||
self.ethcore.ip.tx.fsm,
|
|
||||||
|
|
||||||
self.ethcore.udp.rx.fsm,
|
|
||||||
self.ethcore.udp.tx.fsm
|
|
||||||
]
|
|
||||||
self.submodules.analyzer = LiteScopeAnalyzer(analyzer_signals, 4096, csr_csv="test/analyzer.csv")
|
|
||||||
self.add_csr("analyzer")
|
|
||||||
|
|
||||||
default_subtarget = BaseSoC
|
|
|
@ -1,68 +0,0 @@
|
||||||
#
|
|
||||||
# This file is part of LiteEth.
|
|
||||||
#
|
|
||||||
# Copyright (c) 2015-2019 Florent Kermarrec <florent@enjoy-digital.fr>
|
|
||||||
# SPDX-License-Identifier: BSD-2-Clause
|
|
||||||
|
|
||||||
from liteeth.common import *
|
|
||||||
from liteeth.frontend.etherbone import LiteEthEtherbone
|
|
||||||
|
|
||||||
from targets.base import BaseSoC
|
|
||||||
|
|
||||||
# EtherboneSoC -------------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
class EtherboneSoC(BaseSoC):
|
|
||||||
default_platform = "kc705"
|
|
||||||
def __init__(self, platform):
|
|
||||||
BaseSoC.__init__(self, platform,
|
|
||||||
mac_address=0x10e2d5000000,
|
|
||||||
ip_address="192.168.1.50")
|
|
||||||
self.submodules.etherbone = LiteEthEtherbone(self.ethcore.udp, 1234, mode="master")
|
|
||||||
self.add_wb_master(self.etherbone.wishbone.bus)
|
|
||||||
|
|
||||||
# EtherboneSoCDevel --------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
class EtherboneSoCDevel(EtherboneSoC):
|
|
||||||
def __init__(self, platform):
|
|
||||||
from litescope import LiteScopeAnalyzer
|
|
||||||
EtherboneSoC.__init__(self, platform)
|
|
||||||
debug = [
|
|
||||||
# MMAP stream from HOST
|
|
||||||
self.etherbone.wishbone.sink.valid,
|
|
||||||
self.etherbone.wishbone.sink.last,
|
|
||||||
self.etherbone.wishbone.sink.ready,
|
|
||||||
self.etherbone.wishbone.sink.we,
|
|
||||||
self.etherbone.wishbone.sink.count,
|
|
||||||
self.etherbone.wishbone.sink.base_addr,
|
|
||||||
self.etherbone.wishbone.sink.be,
|
|
||||||
self.etherbone.wishbone.sink.addr,
|
|
||||||
self.etherbone.wishbone.sink.data,
|
|
||||||
|
|
||||||
# MMAP stream to HOST
|
|
||||||
self.etherbone.wishbone.source.valid,
|
|
||||||
self.etherbone.wishbone.source.last,
|
|
||||||
self.etherbone.wishbone.source.ready,
|
|
||||||
self.etherbone.wishbone.source.we,
|
|
||||||
self.etherbone.wishbone.source.count,
|
|
||||||
self.etherbone.wishbone.source.base_addr,
|
|
||||||
self.etherbone.wishbone.source.be,
|
|
||||||
self.etherbone.wishbone.source.addr,
|
|
||||||
self.etherbone.wishbone.source.data,
|
|
||||||
|
|
||||||
# Etherbone wishbone master
|
|
||||||
self.etherbone.wishbone.bus.dat_w,
|
|
||||||
self.etherbone.wishbone.bus.dat_r,
|
|
||||||
self.etherbone.wishbone.bus.adr,
|
|
||||||
self.etherbone.wishbone.bus.sel,
|
|
||||||
self.etherbone.wishbone.bus.cyc,
|
|
||||||
self.etherbone.wishbone.bus.stb,
|
|
||||||
self.etherbone.wishbone.bus.ack,
|
|
||||||
self.etherbone.wishbone.bus.we,
|
|
||||||
self.etherbone.wishbone.bus.cti,
|
|
||||||
self.etherbone.wishbone.bus.bte,
|
|
||||||
self.etherbone.wishbone.bus.err
|
|
||||||
]
|
|
||||||
self.submodules.analyzer = LiteScopeAnalyzer(debug, 4096, csr_csv="test/analyzer.csv")
|
|
||||||
self.add_csr("analyzer")
|
|
||||||
|
|
||||||
default_subtarget = EtherboneSoC
|
|
|
@ -1,41 +0,0 @@
|
||||||
#
|
|
||||||
# This file is part of LiteEth.
|
|
||||||
#
|
|
||||||
# Copyright (c) 2015-2019 Florent Kermarrec <florent@enjoy-digital.fr>
|
|
||||||
# SPDX-License-Identifier: BSD-2-Clause
|
|
||||||
|
|
||||||
from liteeth.common import *
|
|
||||||
from liteeth.frontend.stream import LiteEthUDPStreamer
|
|
||||||
|
|
||||||
from targets.base import BaseSoC
|
|
||||||
|
|
||||||
# StreamSoC ----------------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
class StreamSoC(BaseSoC):
|
|
||||||
default_platform = "kc705"
|
|
||||||
def __init__(self, platform):
|
|
||||||
BaseSoC.__init__(self, platform,
|
|
||||||
mac_address = 0x10e2d5000000,
|
|
||||||
ip_address = "192.168.1.50")
|
|
||||||
self.submodules.streamer = LiteEthUDPStreamer(self.ethcore.udp, convert_ip("192.168.1.100"), 10000)
|
|
||||||
self.comb += self.streamer.source.connect(self.streamer.sink)
|
|
||||||
|
|
||||||
# StreamSoDevel ------------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
class StreamSoCDevel(StreamSoC):
|
|
||||||
def __init__(self, platform):
|
|
||||||
from litescope import LiteScopeAnalyzer
|
|
||||||
StreamSoC.__init__(self, platform)
|
|
||||||
analyzer_signals = [
|
|
||||||
self.streamer.sink.valid,
|
|
||||||
self.streamer.sink.ready,
|
|
||||||
self.streamer.sink.data,
|
|
||||||
|
|
||||||
self.streamer.source.valid,
|
|
||||||
self.streamer.source.ready,
|
|
||||||
self.streamer.source.data
|
|
||||||
]
|
|
||||||
self.submodules.analyzer = LiteScopeAnalyzer(analyzer_signals, 4096, csr_csv="test/analyzer.csv")
|
|
||||||
self.add_csr("analyzer")
|
|
||||||
|
|
||||||
default_subtarget = StreamSoC
|
|
|
@ -1,65 +0,0 @@
|
||||||
#
|
|
||||||
# This file is part of LiteEth.
|
|
||||||
#
|
|
||||||
# Copyright (c) 2015-2019 Florent Kermarrec <florent@enjoy-digital.fr>
|
|
||||||
# SPDX-License-Identifier: BSD-2-Clause
|
|
||||||
|
|
||||||
from liteeth.common import *
|
|
||||||
|
|
||||||
from targets.base import BaseSoC
|
|
||||||
|
|
||||||
# UDPSoC -------------------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
class UDPSoC(BaseSoC):
|
|
||||||
default_platform = "kc705"
|
|
||||||
def __init__(self, platform):
|
|
||||||
BaseSoC.__init__(self, platform,
|
|
||||||
mac_address=0x10e2d5000000,
|
|
||||||
ip_address="192.168.1.50")
|
|
||||||
|
|
||||||
# add udp loopback on port 6000 with dw=8
|
|
||||||
self.add_udp_loopback(6000, 8, 8192, "loopback_8")
|
|
||||||
# add udp loopback on port 8000 with dw=32
|
|
||||||
self.add_udp_loopback(8000, 32, 8192, "loopback_32")
|
|
||||||
|
|
||||||
def add_udp_loopback(self, port, dw, depth, name=None):
|
|
||||||
port = self.ethcore.udp.crossbar.get_port(port, dw)
|
|
||||||
buf = stream.SyncFIFO(eth_udp_user_description(dw), depth//(dw//8))
|
|
||||||
if name is None:
|
|
||||||
self.submodules += buf
|
|
||||||
else:
|
|
||||||
setattr(self.submodules, name, buf)
|
|
||||||
self.comb += port.source.connect(buf.sink)
|
|
||||||
self.comb += buf.source.connect(port.sink)
|
|
||||||
|
|
||||||
# UDPSoCDevel --------------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
class UDPSoCDevel(UDPSoC):
|
|
||||||
def __init__(self, platform):
|
|
||||||
from litescope import LiteScopeAnalyzer
|
|
||||||
UDPSoC.__init__(self, platform)
|
|
||||||
analyzer_signals = [
|
|
||||||
self.loopback_8.sink.valid,
|
|
||||||
self.loopback_8.sink.last,
|
|
||||||
self.loopback_8.sink.ready,
|
|
||||||
self.loopback_8.sink.data,
|
|
||||||
|
|
||||||
self.loopback_8.source.valid,
|
|
||||||
self.loopback_8.source.last,
|
|
||||||
self.loopback_8.source.ready,
|
|
||||||
self.loopback_8.source.data,
|
|
||||||
|
|
||||||
self.loopback_32.sink.valid,
|
|
||||||
self.loopback_32.sink.last,
|
|
||||||
self.loopback_32.sink.ready,
|
|
||||||
self.loopback_32.sink.data,
|
|
||||||
|
|
||||||
self.loopback_32.source.valid,
|
|
||||||
self.loopback_32.source.last,
|
|
||||||
self.loopback_32.source.ready,
|
|
||||||
self.loopback_32.source.data
|
|
||||||
]
|
|
||||||
self.submodules.analyzer = LiteScopeAnalyzer(analyzer_signals, 4096, csr_csv="test/analyzer.csv")
|
|
||||||
self.add_csr("analyzer")
|
|
||||||
|
|
||||||
default_subtarget = UDPSoC
|
|
|
@ -1,33 +0,0 @@
|
||||||
## Purpose
|
|
||||||
Example of UDP loopback with LiteETH IP/UDP hardware stack. The FPGA will echo back any UDP packet it
|
|
||||||
receives on the specified port (default=8000) to the sender.
|
|
||||||
|
|
||||||
You can also found a detailed tutorial [here](https://yehowshuaimmanuel.com/fpga/migen/ethernet_ecp5/).
|
|
||||||
|
|
||||||
## Usage
|
|
||||||
#!bash
|
|
||||||
./versa_ecp5.py
|
|
||||||
./versa_ecp5.py load
|
|
||||||
|
|
||||||
The IP address assigned to the FPGA in this example is ``192.168.1.50`` and the Host is expected to
|
|
||||||
be configured with ``192.168.1.100``. Since ``192.168.1.XXX`` is a common address in home networks, a
|
|
||||||
collisiton is quite possible and in this case, you will need to re-configure the FPGA and the python
|
|
||||||
scripts accordingly.
|
|
||||||
|
|
||||||
Once you are able to ping the FPGA board from your computer, you can run the sender and listener scripts
|
|
||||||
and should see the date/time UDP packets emitted by the sender looped back to the the listener:
|
|
||||||
|
|
||||||
#!bash
|
|
||||||
$python3 listener.py &
|
|
||||||
$python3 sender.py
|
|
||||||
|
|
||||||
2019-11-20 08:31:00
|
|
||||||
2019-11-20 08:31:01
|
|
||||||
2019-11-20 08:31:01
|
|
||||||
2019-11-20 08:31:02
|
|
||||||
2019-11-20 08:31:02
|
|
||||||
2019-11-20 08:31:03
|
|
||||||
2019-11-20 08:31:03
|
|
||||||
2019-11-20 08:31:04
|
|
||||||
2019-11-20 08:31:04
|
|
||||||
[...]
|
|
|
@ -1,19 +0,0 @@
|
||||||
#!/usr/bin/env python3
|
|
||||||
|
|
||||||
#
|
|
||||||
# This file is part of LiteEth.
|
|
||||||
#
|
|
||||||
# Copyright (c) 2019 Yehowshua Immanuel <yimmanuel3@gatech.edu>
|
|
||||||
# SPDX-License-Identifier: BSD-2-Clause
|
|
||||||
|
|
||||||
import socket
|
|
||||||
|
|
||||||
UDP_IP = "192.168.1.50"
|
|
||||||
UDP_PORT = 8000
|
|
||||||
|
|
||||||
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
|
||||||
sock.bind((UDP_IP, UDP_PORT))
|
|
||||||
|
|
||||||
while True:
|
|
||||||
data, addr = sock.recvfrom(1024)
|
|
||||||
print(data.decode("utf-8"))
|
|
|
@ -1,22 +0,0 @@
|
||||||
#!/usr/bin/env python3
|
|
||||||
|
|
||||||
#
|
|
||||||
# This file is part of LiteEth.
|
|
||||||
#
|
|
||||||
# Copyright (c) 2019 Yehowshua Immanuel <yimmanuel3@gatech.edu>
|
|
||||||
# SPDX-License-Identifier: BSD-2-Clause
|
|
||||||
|
|
||||||
import socket
|
|
||||||
import time
|
|
||||||
import datetime
|
|
||||||
|
|
||||||
UDP_IP = "192.168.1.100"
|
|
||||||
UDP_PORT = 8000
|
|
||||||
|
|
||||||
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
|
||||||
|
|
||||||
while True:
|
|
||||||
t = datetime.datetime.fromtimestamp(time.time()).strftime("%Y-%m-%d %H:%M:%S")
|
|
||||||
print(t)
|
|
||||||
sock.sendto(t.encode('utf-8'), (UDP_IP, UDP_PORT))
|
|
||||||
time.sleep(0.5)
|
|
|
@ -1,121 +0,0 @@
|
||||||
#!/usr/bin/env python3
|
|
||||||
|
|
||||||
#
|
|
||||||
# This file is part of LiteEth.
|
|
||||||
#
|
|
||||||
# Copyright (c) 2019 Yehowshua Immanuel <yimmanuel3@gatech.edu>
|
|
||||||
# Copyright (c) 2019 Florent Kermarrec <florent@enjoy-digital.fr>
|
|
||||||
# SPDX-License-Identifier: BSD-2-Clause
|
|
||||||
|
|
||||||
import sys
|
|
||||||
|
|
||||||
from migen import *
|
|
||||||
|
|
||||||
from litex.build.generic_platform import *
|
|
||||||
|
|
||||||
from litex.boards.platforms import versa_ecp5
|
|
||||||
|
|
||||||
from litex.soc.cores.clock import *
|
|
||||||
from litex.soc.integration.soc_core import *
|
|
||||||
from litex.soc.integration.builder import *
|
|
||||||
|
|
||||||
from liteeth.common import *
|
|
||||||
from liteeth.phy.ecp5rgmii import LiteEthPHYRGMII
|
|
||||||
from liteeth.core import LiteEthUDPIPCore
|
|
||||||
|
|
||||||
# CRG ----------------------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
class _CRG(Module):
|
|
||||||
def __init__(self, platform, sys_clk_freq):
|
|
||||||
self.clock_domains.cd_sys = ClockDomain()
|
|
||||||
|
|
||||||
# # #
|
|
||||||
|
|
||||||
self.cd_sys.clk.attr.add("keep")
|
|
||||||
|
|
||||||
# clk / rst
|
|
||||||
clk100 = platform.request("clk100")
|
|
||||||
platform.add_period_constraint(clk100, 1e9/100e6)
|
|
||||||
|
|
||||||
# pll
|
|
||||||
self.submodules.pll = pll = ECP5PLL()
|
|
||||||
pll.register_clkin(clk100, 100e6)
|
|
||||||
pll.create_clkout(self.cd_sys, sys_clk_freq)
|
|
||||||
|
|
||||||
|
|
||||||
# UDPLoopback ------------------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
class UDPLoopback(SoCMini):
|
|
||||||
def __init__(self, platform):
|
|
||||||
|
|
||||||
sys_clk_freq = int(150e6)
|
|
||||||
SoCMini.__init__(self, platform, sys_clk_freq, ident="UDPLoopback", ident_version=True)
|
|
||||||
|
|
||||||
# CRG --------------------------------------------------------------------------------------
|
|
||||||
self.submodules.crg = _CRG(platform, sys_clk_freq)
|
|
||||||
|
|
||||||
# Ethernet ---------------------------------------------------------------------------------
|
|
||||||
# phy
|
|
||||||
self.submodules.eth_phy = LiteEthPHYRGMII(
|
|
||||||
clock_pads = platform.request("eth_clocks"),
|
|
||||||
pads = platform.request("eth"))
|
|
||||||
self.add_csr("eth_phy")
|
|
||||||
# core
|
|
||||||
self.submodules.eth_core = LiteEthUDPIPCore(
|
|
||||||
phy = self.eth_phy,
|
|
||||||
mac_address = 0x10e2d5000000,
|
|
||||||
ip_address = "192.168.1.50",
|
|
||||||
clk_freq = sys_clk_freq)
|
|
||||||
|
|
||||||
# add udp loopback on port 6000 with dw=8
|
|
||||||
self.add_udp_loopback(6000, 8, 128, "loopback_8")
|
|
||||||
# add udp loopback on port 8000 with dw=32
|
|
||||||
self.add_udp_loopback(8000, 32, 128, "loopback_32")
|
|
||||||
|
|
||||||
# timing constraints
|
|
||||||
self.eth_phy.crg.cd_eth_rx.clk.attr.add("keep")
|
|
||||||
self.eth_phy.crg.cd_eth_tx.clk.attr.add("keep")
|
|
||||||
self.platform.add_period_constraint(self.eth_phy.crg.cd_eth_rx.clk, 1e9/125e6)
|
|
||||||
self.platform.add_period_constraint(self.eth_phy.crg.cd_eth_tx.clk, 1e9/125e6)
|
|
||||||
|
|
||||||
def add_udp_loopback(self, port, dw, depth, name=None):
|
|
||||||
port = self.eth_core.udp.crossbar.get_port(port, dw)
|
|
||||||
buf = stream.SyncFIFO(eth_udp_user_description(dw), depth//(dw//8))
|
|
||||||
if name is None:
|
|
||||||
self.submodules += buf
|
|
||||||
else:
|
|
||||||
setattr(self.submodules, name, buf)
|
|
||||||
self.comb += port.source.connect(buf.sink)
|
|
||||||
self.comb += buf.source.connect(port.sink)
|
|
||||||
|
|
||||||
# Load ---------------------------------------------------------------------------------------------
|
|
||||||
def load():
|
|
||||||
import os
|
|
||||||
f = open("ecp5-versa5g.cfg", "w")
|
|
||||||
f.write(
|
|
||||||
"""
|
|
||||||
interface ftdi
|
|
||||||
ftdi_vid_pid 0x0403 0x6010
|
|
||||||
ftdi_channel 0
|
|
||||||
ftdi_layout_init 0xfff8 0xfffb
|
|
||||||
reset_config none
|
|
||||||
adapter_khz 25000
|
|
||||||
jtag newtap ecp5 tap -irlen 8 -expected-id 0x81112043
|
|
||||||
""")
|
|
||||||
f.close()
|
|
||||||
os.system("openocd -f ecp5-versa5g.cfg -c \"transport select jtag; init; svf build/gateware/top.svf; exit\"")
|
|
||||||
|
|
||||||
# Build --------------------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
def main():
|
|
||||||
if "load" in sys.argv[1:]:
|
|
||||||
load()
|
|
||||||
exit()
|
|
||||||
else:
|
|
||||||
platform = versa_ecp5.Platform(toolchain="trellis")
|
|
||||||
soc = UDPLoopback(platform)
|
|
||||||
builder = Builder(soc, output_dir="build", csr_csv="tools/csr.csv")
|
|
||||||
vns = builder.build()
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
main()
|
|
|
@ -1,27 +0,0 @@
|
||||||
#!/usr/bin/env python3
|
|
||||||
|
|
||||||
#
|
|
||||||
# This file is part of LiteEth.
|
|
||||||
#
|
|
||||||
# Copyright (c) 2015-2018 Florent Kermarrec <florent@enjoy-digital.fr>
|
|
||||||
# SPDX-License-Identifier: BSD-2-Clause
|
|
||||||
|
|
||||||
from litex import RemoteClient
|
|
||||||
|
|
||||||
wb = RemoteClient()
|
|
||||||
wb.open()
|
|
||||||
|
|
||||||
# # #
|
|
||||||
|
|
||||||
from litescope.software.driver.analyzer import LiteScopeAnalyzerDriver
|
|
||||||
analyzer = LiteScopeAnalyzerDriver(wb.regs, "analyzer", debug=True)
|
|
||||||
analyzer.configure_trigger(cond={})
|
|
||||||
analyzer.configure_subsampler(1)
|
|
||||||
analyzer.run(offset=128, length=256)
|
|
||||||
analyzer.wait_done()
|
|
||||||
analyzer.upload()
|
|
||||||
analyzer.save("dump.vcd")
|
|
||||||
|
|
||||||
# # #
|
|
||||||
|
|
||||||
wb.close()
|
|
|
@ -1,46 +0,0 @@
|
||||||
#
|
|
||||||
# This file is part of LiteEth.
|
|
||||||
#
|
|
||||||
# Copyright (c) 2015-2018 Florent Kermarrec <florent@enjoy-digital.fr>
|
|
||||||
# SPDX-License-Identifier: BSD-2-Clause
|
|
||||||
|
|
||||||
import socket
|
|
||||||
import time
|
|
||||||
from litex.tools.remote.etherbone import *
|
|
||||||
|
|
||||||
SRAM_BASE = 0x01000000
|
|
||||||
|
|
||||||
socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
|
||||||
|
|
||||||
# test probe
|
|
||||||
packet = EtherbonePacket()
|
|
||||||
packet.pf = 1
|
|
||||||
packet.encode()
|
|
||||||
socket.sendto(bytes(packet), ("192.168.1.50", 20000))
|
|
||||||
time.sleep(0.01)
|
|
||||||
|
|
||||||
# test writes
|
|
||||||
writes_datas = [j for j in range(16)]
|
|
||||||
writes = EtherboneWrites(base_addr=SRAM_BASE, datas=writes_datas)
|
|
||||||
record = EtherboneRecord()
|
|
||||||
record.writes = writes
|
|
||||||
record.wcount = len(writes_datas)
|
|
||||||
|
|
||||||
packet = EtherbonePacket()
|
|
||||||
packet.records = [record]
|
|
||||||
packet.encode()
|
|
||||||
socket.sendto(bytes(packet), ("192.168.1.50", 20000))
|
|
||||||
time.sleep(0.01)
|
|
||||||
|
|
||||||
# test reads
|
|
||||||
reads_addrs = [SRAM_BASE+4*j for j in range(16)]
|
|
||||||
reads = EtherboneReads(base_ret_addr=0x1000, addrs=reads_addrs)
|
|
||||||
record = EtherboneRecord()
|
|
||||||
record.reads = reads
|
|
||||||
record.rcount = len(reads_addrs)
|
|
||||||
|
|
||||||
packet = EtherbonePacket()
|
|
||||||
packet.records = [record]
|
|
||||||
packet.encode()
|
|
||||||
socket.sendto(bytes(packet), ("192.168.1.50", 20000))
|
|
||||||
time.sleep(0.01)
|
|
|
@ -1,26 +0,0 @@
|
||||||
#
|
|
||||||
# This file is part of LiteEth.
|
|
||||||
#
|
|
||||||
# Copyright (c) 2015-2018 Florent Kermarrec <florent@enjoy-digital.fr>
|
|
||||||
# SPDX-License-Identifier: BSD-2-Clause
|
|
||||||
|
|
||||||
from litex import RemoteClient
|
|
||||||
|
|
||||||
wb = RemoteClient()
|
|
||||||
wb.open()
|
|
||||||
|
|
||||||
# # #
|
|
||||||
|
|
||||||
identifier = ""
|
|
||||||
for i in range(30):
|
|
||||||
identifier += chr(wb.read(wb.bases.identifier_mem + 4*(i+1))) # TODO: why + 1?
|
|
||||||
print(identifier)
|
|
||||||
print("frequency : {}MHz".format(wb.constants.system_clock_frequency/1000000))
|
|
||||||
|
|
||||||
SRAM_BASE = 0x02000000
|
|
||||||
wb.write(SRAM_BASE, [i for i in range(64)])
|
|
||||||
print(wb.read(SRAM_BASE, 64))
|
|
||||||
|
|
||||||
# # #
|
|
||||||
|
|
||||||
wb.close()
|
|
|
@ -1,47 +0,0 @@
|
||||||
#
|
|
||||||
# This file is part of LiteEth.
|
|
||||||
#
|
|
||||||
# Copyright (c) 2015-2018 Florent Kermarrec <florent@enjoy-digital.fr>
|
|
||||||
# SPDX-License-Identifier: BSD-2-Clause
|
|
||||||
|
|
||||||
import socket
|
|
||||||
import threading
|
|
||||||
|
|
||||||
|
|
||||||
def test(fpga_ip, udp_port, test_message):
|
|
||||||
tx_sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
|
||||||
rx_sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
|
||||||
rx_sock.bind(("", udp_port))
|
|
||||||
rx_sock.settimeout(0.5)
|
|
||||||
|
|
||||||
def receive():
|
|
||||||
while True:
|
|
||||||
try:
|
|
||||||
msg = rx_sock.recv(8192)
|
|
||||||
for byte in msg:
|
|
||||||
print(chr(byte), end="")
|
|
||||||
except:
|
|
||||||
break
|
|
||||||
|
|
||||||
def send():
|
|
||||||
tx_sock.sendto(bytes(test_message, "utf-8"), (fpga_ip, udp_port))
|
|
||||||
|
|
||||||
receive_thread = threading.Thread(target=receive)
|
|
||||||
receive_thread.start()
|
|
||||||
|
|
||||||
send_thread = threading.Thread(target=send)
|
|
||||||
send_thread.start()
|
|
||||||
|
|
||||||
try:
|
|
||||||
send_thread.join(5)
|
|
||||||
send_thread.join(5)
|
|
||||||
except KeyboardInterrupt:
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
# # #
|
|
||||||
|
|
||||||
test_message = "LiteEth Stream Hello world\n"
|
|
||||||
test("192.168.1.50", 10000, test_message)
|
|
||||||
|
|
||||||
# # #
|
|
|
@ -1,94 +0,0 @@
|
||||||
#
|
|
||||||
# This file is part of LiteEth.
|
|
||||||
#
|
|
||||||
# Copyright (c) 2015-2018 Florent Kermarrec <florent@enjoy-digital.fr>
|
|
||||||
# SPDX-License-Identifier: BSD-2-Clause
|
|
||||||
|
|
||||||
import socket
|
|
||||||
import time
|
|
||||||
import threading
|
|
||||||
import copy
|
|
||||||
|
|
||||||
KB = 1024
|
|
||||||
MB = 1024*KB
|
|
||||||
GB = 1024*MB
|
|
||||||
|
|
||||||
|
|
||||||
def seed_to_data(seed, random=True):
|
|
||||||
if random:
|
|
||||||
return (seed * 0x31415979 + 1) & 0xffffffff
|
|
||||||
else:
|
|
||||||
return seed
|
|
||||||
|
|
||||||
|
|
||||||
def check(p1, p2):
|
|
||||||
p1 = copy.deepcopy(p1)
|
|
||||||
p2 = copy.deepcopy(p2)
|
|
||||||
if isinstance(p1, int):
|
|
||||||
return 0, 1, int(p1 != p2)
|
|
||||||
else:
|
|
||||||
if len(p1) >= len(p2):
|
|
||||||
ref, res = p1, p2
|
|
||||||
else:
|
|
||||||
ref, res = p2, p1
|
|
||||||
shift = 0
|
|
||||||
while((ref[0] != res[0]) and (len(res) > 1)):
|
|
||||||
res.pop(0)
|
|
||||||
shift += 1
|
|
||||||
length = min(len(ref), len(res))
|
|
||||||
errors = 0
|
|
||||||
for i in range(length):
|
|
||||||
if ref.pop(0) != res.pop(0):
|
|
||||||
errors += 1
|
|
||||||
return shift, length, errors
|
|
||||||
|
|
||||||
|
|
||||||
def generate_packet(seed, length):
|
|
||||||
r = []
|
|
||||||
for i in range(length):
|
|
||||||
r.append(seed_to_data(seed, True)%0xff) # XXX FIXME
|
|
||||||
seed += 1
|
|
||||||
return r, seed
|
|
||||||
|
|
||||||
|
|
||||||
def test(fpga_ip, udp_port, test_size):
|
|
||||||
tx_sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
|
||||||
rx_sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
|
||||||
rx_sock.bind(("", udp_port))
|
|
||||||
|
|
||||||
def receive():
|
|
||||||
rx_seed = 0
|
|
||||||
while rx_seed < test_size:
|
|
||||||
data, addr = rx_sock.recvfrom(8192)
|
|
||||||
rx_packet = []
|
|
||||||
for byte in data:
|
|
||||||
rx_packet.append(int(byte))
|
|
||||||
rx_reference_packet, rx_seed = generate_packet(rx_seed, 1024)
|
|
||||||
s, l, e = check(rx_reference_packet, rx_packet)
|
|
||||||
print("shift " + str(s) + " / length " + str(l) + " / errors " + str(e))
|
|
||||||
|
|
||||||
def send():
|
|
||||||
tx_seed = 0
|
|
||||||
while tx_seed < test_size:
|
|
||||||
tx_packet, tx_seed = generate_packet(tx_seed, 1024)
|
|
||||||
tx_sock.sendto(bytes(tx_packet), (fpga_ip, udp_port))
|
|
||||||
time.sleep(0.001) # XXX: FIXME, Python limitation?
|
|
||||||
|
|
||||||
receive_thread = threading.Thread(target=receive)
|
|
||||||
receive_thread.start()
|
|
||||||
|
|
||||||
send_thread = threading.Thread(target=send)
|
|
||||||
send_thread.start()
|
|
||||||
|
|
||||||
try:
|
|
||||||
send_thread.join(10)
|
|
||||||
receive_thread.join(0.1)
|
|
||||||
except KeyboardInterrupt:
|
|
||||||
pass
|
|
||||||
|
|
||||||
# # #
|
|
||||||
|
|
||||||
test("192.168.1.50", 6000, 128*KB)
|
|
||||||
test("192.168.1.50", 8000, 128*KB)
|
|
||||||
|
|
||||||
# # #
|
|
|
@ -1,39 +0,0 @@
|
||||||
#
|
|
||||||
# This file is part of LiteEth.
|
|
||||||
#
|
|
||||||
# Copyright (c) 2019 Florent Kermarrec <florent@enjoy-digital.fr>
|
|
||||||
# SPDX-License-Identifier: BSD-2-Clause
|
|
||||||
|
|
||||||
import unittest
|
|
||||||
import os
|
|
||||||
|
|
||||||
root_dir = os.path.join(os.path.abspath(os.path.dirname(__file__)), "..")
|
|
||||||
make_script = os.path.join(root_dir, "examples", "make.py")
|
|
||||||
|
|
||||||
class TestExamples(unittest.TestCase):
|
|
||||||
def example_test(self, t, s):
|
|
||||||
os.system("rm -rf {}/build".format(root_dir))
|
|
||||||
cmd = "python3 " + make_script + " "
|
|
||||||
cmd += "-t {} ".format(t)
|
|
||||||
cmd += "-s {} ".format(s)
|
|
||||||
cmd += "-p kc705 "
|
|
||||||
cmd += "-Ob run False "
|
|
||||||
cmd += "build-bitstream"
|
|
||||||
os.system(cmd)
|
|
||||||
self.assertEqual(os.path.isfile("{}/build/{}_kc705.v".format(root_dir, s.lower())), True)
|
|
||||||
|
|
||||||
def test_base_example(self):
|
|
||||||
self.example_test("base", "BaseSoC")
|
|
||||||
self.example_test("base", "BaseSoCDevel")
|
|
||||||
|
|
||||||
def test_udp_example(self):
|
|
||||||
self.example_test("udp", "UDPSoC")
|
|
||||||
self.example_test("udp", "UDPSoCDevel")
|
|
||||||
|
|
||||||
def test_etherbone_example(self):
|
|
||||||
self.example_test("etherbone", "EtherboneSoC")
|
|
||||||
self.example_test("etherbone", "EtherboneSoCDevel")
|
|
||||||
|
|
||||||
def test_stream_example(self):
|
|
||||||
self.example_test("stream", "StreamSoC")
|
|
||||||
self.example_test("stream", "StreamSoCDevel")
|
|
Loading…
Reference in New Issue