diff --git a/examples/targets/udp_loopback/README.md b/examples/targets/udp_loopback/README.md new file mode 100644 index 0000000..21f1354 --- /dev/null +++ b/examples/targets/udp_loopback/README.md @@ -0,0 +1,33 @@ +## 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 + [...] diff --git a/examples/targets/udp_loopback/listener.py b/examples/targets/udp_loopback/listener.py new file mode 100755 index 0000000..5c6d6a6 --- /dev/null +++ b/examples/targets/udp_loopback/listener.py @@ -0,0 +1,16 @@ +#!/usr/bin/env python3 + +# This file is Copyright (c) 2019 Yehowshua Immanuel +# License: BSD + +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")) diff --git a/examples/targets/udp_loopback/sender.py b/examples/targets/udp_loopback/sender.py new file mode 100755 index 0000000..cba27de --- /dev/null +++ b/examples/targets/udp_loopback/sender.py @@ -0,0 +1,19 @@ +#!/usr/bin/env python3 + +# This file is Copyright (c) 2019 Yehowshua Immanuel +# License: BSD + +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) diff --git a/examples/targets/udp_loopback/versa_ecp5.py b/examples/targets/udp_loopback/versa_ecp5.py new file mode 100755 index 0000000..57d10ef --- /dev/null +++ b/examples/targets/udp_loopback/versa_ecp5.py @@ -0,0 +1,118 @@ +#!/usr/bin/env python3 + +# This file is Copyright (c) 2019 Yehowshua Immanuel +# This file is Copyright (c) 2019 Florent Kermarrec +# License: BSD + +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.connect(port, buf) + + +# 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() diff --git a/examples/targets/versa_ecp5_udp_loopback/README.md b/examples/targets/versa_ecp5_udp_loopback/README.md deleted file mode 100644 index 61e48b2..0000000 --- a/examples/targets/versa_ecp5_udp_loopback/README.md +++ /dev/null @@ -1,46 +0,0 @@ -## Purpose -Example of UDP loopback on versa ECP5 FPGA using the Liteeth UDP module. -The FPGA will echo back any UDP packet it recieves over RJ45 ethernet to the sender. - -You can also view a rather detailed tutorial [here](https://yehowshuaimmanuel.com/fpga/migen/ethernet_ecp5/). - -## Usage - - #!bash - ./udp.py build - ./udp.py load - -You will have to configure your ARP table manually since this example does not instantiate the Liteeth ARP module table. -The IP address assigned to the FPGA in this example is ``169.253.2.100``. You should make sure that your computer's -ethernet interface is on the same subnet, for example: - - #!bash - $ifconfig en7 192.168.1.100 netmask 255.255.255.0 - -And then after that configure your ARP table: - - #!bash - $arp -s 192.168.1.50 10:e2:d5:00:00:00 -iface en7 - -You should now be able to send and recieve UDP packets. - - #!bash - $python3 listener.py & - $python3 sender.py - - UDP target IP:192.168.1.50 - UDP target port:8000 - message:Hey. - received message:b'Heyn' - -If everything is working, you should be able to see the ``received message`` line as shown above. - -## Possible Problems - -192.168.1.XXX is a common address in home networks a collsion is quite possible. - -In particular, some ARP daemons will automatically add a higher priority duplicate IP entry for 192.168.1.50 to the Wi-Fi -interface making it challenging to route packets with the ethernet as the gateway. - -To get around this, change the IP address of your ethernet and FPGA to something different from 192. You must change this -in in all the Python files in this directory. diff --git a/examples/targets/versa_ecp5_udp_loopback/listener.py b/examples/targets/versa_ecp5_udp_loopback/listener.py deleted file mode 100644 index b0bada2..0000000 --- a/examples/targets/versa_ecp5_udp_loopback/listener.py +++ /dev/null @@ -1,15 +0,0 @@ -# This file is Copyright (c) 2019 Yehowshua Immanuel -# License: BSD - -import socket - -UDP_IP = "192.168.1.100" -UDP_PORT = 8000 - -sock = socket.socket(socket.AF_INET, # Internet - socket.SOCK_DGRAM) # UDP -sock.bind((UDP_IP, UDP_PORT)) - -while True: - data, addr = sock.recvfrom(1024) # buffer size is 1024 bytes - print("received message:" + str(data)) diff --git a/examples/targets/versa_ecp5_udp_loopback/sender.py b/examples/targets/versa_ecp5_udp_loopback/sender.py deleted file mode 100644 index 70a10cf..0000000 --- a/examples/targets/versa_ecp5_udp_loopback/sender.py +++ /dev/null @@ -1,15 +0,0 @@ -# This file is Copyright (c) 2019 Yehowshua Immanuel -# License: BSD -import socket - -UDP_IP = "192.168.1.50" -UDP_PORT = 8000 -MESSAGE = "Hey." - -print("UDP target IP:" + str(UDP_IP)) -print("UDP target port:" + str(UDP_PORT)) -print("message:" + str(MESSAGE)) - -sock = socket.socket(socket.AF_INET, # Internet - socket.SOCK_DGRAM) # UDP -sock.sendto(MESSAGE.encode('utf-8'), (UDP_IP, UDP_PORT)) diff --git a/examples/targets/versa_ecp5_udp_loopback/udp.py b/examples/targets/versa_ecp5_udp_loopback/udp.py deleted file mode 100755 index a4b0d0f..0000000 --- a/examples/targets/versa_ecp5_udp_loopback/udp.py +++ /dev/null @@ -1,124 +0,0 @@ -#!/usr/bin/env python3 - -# This file is Copyright (c) 2019 Florent Kermarrec -# This file is Copyright (c) 2019 Yehowshua Immanuel -# License: BSD - -import sys - -from migen import * -from migen.genlib.resetsync import AsyncResetSynchronizer - -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.phy.ecp5rgmii import LiteEthPHYRGMII -from liteeth.core import LiteEthUDPIPCore -from liteeth.common import * - -# 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 - generates top.lpf - 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.connect(port, buf) - - -# 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() - if "build" in sys.argv[1:]: - platform = versa_ecp5.Platform(toolchain="trellis") - soc = UDPLoopback(platform) - builder = Builder(soc, output_dir="build", csr_csv="tools/csr.csv") - vns = builder.build() - else: - print("Usage:") - print("./udp.py build") - print("./udp.py load") - -if __name__ == "__main__": - main() -