example/udp_loopback: simplify/cleanup and make it more generic
This commit is contained in:
parent
50cc7d0671
commit
e980b603cc
|
@ -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
|
||||
[...]
|
|
@ -0,0 +1,16 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
# This file is Copyright (c) 2019 Yehowshua Immanuel <yimmanuel3@gatech.edu>
|
||||
# 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"))
|
|
@ -0,0 +1,19 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
# This file is Copyright (c) 2019 Yehowshua Immanuel <yimmanuel3@gatech.edu>
|
||||
# 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)
|
|
@ -0,0 +1,118 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
# This file is Copyright (c) 2019 Yehowshua Immanuel <yimmanuel3@gatech.edu>
|
||||
# This file is Copyright (c) 2019 Florent Kermarrec <florent@enjoy-digital.fr>
|
||||
# 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()
|
|
@ -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.
|
|
@ -1,15 +0,0 @@
|
|||
# This file is Copyright (c) 2019 Yehowshua Immanuel <yimmanuel3@gatech.edu>
|
||||
# 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))
|
|
@ -1,15 +0,0 @@
|
|||
# This file is Copyright (c) 2019 Yehowshua Immanuel <yimmanuel3@gatech.edu>
|
||||
# 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))
|
|
@ -1,124 +0,0 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
# This file is Copyright (c) 2019 Florent Kermarrec <florent@enjoy-digital.fr>
|
||||
# This file is Copyright (c) 2019 Yehowshua Immanuel <yimmanuel3@gatech.edu>
|
||||
# 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()
|
||||
|
Loading…
Reference in New Issue