Loading files
This file is pinned to the selected release. Download the original to open it in your design tool.
#!/usr/bin/env python3
#
# This file is part of LiteX.
#
# Copyright (c) 2015-2021 Florent Kermarrec <florent@enjoy-digital.fr>
# Copyright (c) 2019 Sean Cross <sean@xobs.io>
# Copyright (c) 2018 Felix Held <felix-github@felixheld.de>
# SPDX-License-Identifier: BSD-2-Clause
import argparse
import os
import sys
import socket
import threading
from litex.tools.remote.etherbone import EtherbonePacket, EtherboneRecord, EtherboneWrites
from litex.tools.remote.etherbone import EtherboneIPC
# Read Merger --------------------------------------------------------------------------------------
def _read_merger(addrs, max_length=255, bursts=None):
"""Sequential reads merger
Take a list of read addresses as input and merge the sequential/fixed reads in (base, length, burst) tuples:
Example: [0x0, 0x4, 0x10, 0x14, 0x20, 0x20] input will return [(0x0,2, "incr"), (0x10,2, "incr"), (0x20,2, "fixed")].
This is useful for UARTBone/Etherbone where command/response roundtrip delay is responsible for
most of the access delay and allows minimizing number of commands by grouping them in UARTBone
packets.
"""
bursts = ["incr", "fixed"] if bursts is None else bursts
if max_length <= 0:
raise ValueError("max_length must be greater than 0.")
if "incr" not in bursts:
raise ValueError("Read merger requires incr burst support.")
for burst in bursts:
if burst not in ["incr", "fixed"]:
raise ValueError("Unsupported burst mode: {}".format(burst))
if not addrs:
return
burst_base = addrs[0]
burst_length = 1
burst_type = "incr"
for addr in addrs[1:]:
merged = False
# Try to merge to a "fixed" burst if supported
if ("fixed" in bursts):
# If current burst matches
if (burst_type == "fixed") or (burst_length == 1):
# If addr matches
if (addr == burst_base):
if (burst_length != max_length):
burst_type = "fixed"
burst_length += 1
merged = True
# Try to merge to an "incr" burst if supported
if ("incr" in bursts):
# If current burst matches
if (burst_type == "incr") or (burst_length == 1):
# If addr matches
if (addr == burst_base + (4 * burst_length)):
if (burst_length != max_length):
burst_type = "incr"
burst_length += 1
merged = True
# Generate current burst if addr has not able to merge
if not merged:
yield (burst_base, burst_length, burst_type)
burst_base = addr
burst_length = 1
burst_type = "incr"
yield (burst_base, burst_length, burst_type)
# Remote Server ------------------------------------------------------------------------------------
class RemoteServer(EtherboneIPC):
def __init__(self, comm, bind_ip, bind_port=1234, addr_width=32):
self.comm = comm
self.bind_ip = bind_ip
self.bind_port = bind_port
self.lock = threading.Lock()
self.addr_width = addr_width
self.addr_size = self.addr_width // 8
comm_name = comm.__class__.__name__
self.read_max_length = {
"CommUART": 255,
"CommUDP": 1,
}.get(comm_name, 1)
self.read_bursts = {
"CommUART": ["incr", "fixed"]
}.get(comm_name, ["incr"])
def open(self):
if hasattr(self, "socket"):
return
self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
if hasattr(socket, "SO_REUSEADDR"):
self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
if hasattr(socket, "SO_REUSEPORT"):
self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1)
self.socket.bind((self.bind_ip, self.bind_port))
print("tcp port: {:d}".format(self.bind_port))
self.socket.listen(1)
self.comm.open()
def close(self):
self.comm.close()
if not hasattr(self, "socket"):
return
self.socket.close()
del self.socket
def _send_server_info(self, client_socket):
# FIXME: Formalize info/improve.
info = []
info.append(f"{self.comm.__class__.__name__}")
info.append(f"{self.bind_ip}")
info.append(f"{self.bind_port}")
info = ":".join(info)
client_socket.sendall(bytes(info, "UTF-8"))
def _serve_thread(self):
while True:
client_socket, addr = self.socket.accept()
self._send_server_info(client_socket)
print("Connected with " + addr[0] + ":" + str(addr[1]))
try:
# Serve Etherbone reads/writes.
while True:
# Receive packet.
try:
packet = self.receive_packet(client_socket, self.addr_size)
if packet == 0:
break
except Exception:
break
# Decode Packet.
packet = EtherbonePacket(self.addr_width, packet)
packet.decode()
# Get Packet's Record.
record = packet.records.pop()
# Hardware lock/reservation.
with self.lock:
# Handle Etherbone writes.
if record.writes != None:
self.comm.write(record.writes.base_addr, record.writes.get_datas())
# Handle Etherbone reads.
if record.reads != None:
reads = []
for addr, length, burst in _read_merger(record.reads.get_addrs(),
max_length = self.read_max_length,
bursts = self.read_bursts):
reads.extend(self.comm.read(addr, length, burst))
record = EtherboneRecord(self.addr_size)
record.writes = EtherboneWrites(addr_size=self.addr_size, datas=reads)
record.wcount = len(record.writes)
packet = EtherbonePacket(self.addr_width)
packet.records = [record]
packet.encode()
self.send_packet(client_socket, packet)
finally:
print("Disconnect")
client_socket.close()
def start(self, nthreads):
for i in range(nthreads):
self.serve_thread = threading.Thread(target=self._serve_thread)
self.serve_thread.daemon = True
self.serve_thread.start()
# Run ----------------------------------------------------------------------------------------------
def main():
parser = argparse.ArgumentParser(description="LiteX Server utility", formatter_class=argparse.ArgumentDefaultsHelpFormatter)
# Common arguments
parser.add_argument("--bind-ip", default="localhost", help="Host bind address.")
parser.add_argument("--bind-port", default=1234, help="Host bind port.")
parser.add_argument("--addr-width", default=32, help="bus address width.")
parser.add_argument("--debug", action="store_true", help="Enable debug.")
# UART arguments
parser.add_argument("--uart", action="store_true", help="Select UART interface.")
parser.add_argument("--uart-port", default=None, help="Set UART port.")
parser.add_argument("--uart-baudrate", default=115200, help="Set UART baudrate.")
# JTAG arguments
parser.add_argument("--jtag", action="store_true", help="Select JTAG interface.")
parser.add_argument("--jtag-config", default="openocd_xc7_ft232.cfg", help="OpenOCD JTAG configuration file.")
parser.add_argument("--jtag-chain", default=1, help="JTAG chain.")
# UDP arguments
parser.add_argument("--udp", action="store_true", help="Select UDP interface.")
parser.add_argument("--udp-ip", default="192.168.1.50", help="Set UDP remote IP address.")
parser.add_argument("--udp-port", default=1234, help="Set UDP remote port.")
parser.add_argument("--udp-scan", action="store_true", help="Scan network for available UDP devices.")
# PCIe arguments
parser.add_argument("--pcie", action="store_true", help="Select PCIe interface.")
parser.add_argument("--pcie-bar", default=None, help="Set PCIe BAR.")
# USB arguments
parser.add_argument("--usb", action="store_true", help="Select USB interface.")
parser.add_argument("--usb-vid", default=None, help="Set USB vendor ID.")
parser.add_argument("--usb-pid", default=None, help="Set USB product ID.")
parser.add_argument("--usb-max-retries", default=10, help="Number of USB reconnecting retries.")
# DevMem arguments
parser.add_argument("--devmem", action="store_true", help="Select DevMem interface (/dev/mem on a Hard CPU).")
parser.add_argument("--devmem-base", default="0xff200000", help="Set DevMem window base address.")
parser.add_argument("--devmem-size", default="0x200000", help="Set DevMem window size.")
args = parser.parse_args()
if args.udp_scan:
args.udp = True
interfaces = ["uart", "jtag", "udp", "pcie", "usb", "devmem"]
selected_interfaces = [name for name in interfaces if getattr(args, name)]
if len(selected_interfaces) == 0:
parser.error("select one interface: --uart, --jtag, --udp, --udp-scan, --pcie, --usb or --devmem.")
if len(selected_interfaces) > 1:
parser.error("select only one interface (got: {}).".format(
", ".join("--" + name for name in selected_interfaces)))
# UART mode
if args.uart:
from litex.tools.remote.comm_uart import CommUART
if args.uart_port is None:
parser.error("--uart requires --uart-port.")
uart_port = args.uart_port
uart_baudrate = int(float(args.uart_baudrate))
print("[CommUART] port: {} / baudrate: {} / ".format(uart_port, uart_baudrate), end="")
comm = CommUART(uart_port, uart_baudrate, debug=args.debug, addr_width=int(args.addr_width))
# JTAG mode
elif args.jtag:
from litex.tools.litex_term import JTAGUART
from litex.tools.remote.comm_uart import CommUART
jtag_uart = JTAGUART(config=args.jtag_config, chain=int(args.jtag_chain))
jtag_uart.open()
print("[CommUART] port: JTAG / ", end="")
comm = CommUART(os.ttyname(jtag_uart.name), debug=args.debug, addr_width=int(args.addr_width))
# UDP mode
elif args.udp:
from litex.tools.remote.comm_udp import CommUDP
udp_ip = args.udp_ip
udp_port = int(args.udp_port)
if args.udp_scan:
udp_ip = udp_ip.split(".")
if len(udp_ip) != 4:
parser.error("--udp-scan requires --udp-ip to contain four octets.")
udp_ip[3] = "x"
udp_ip = ".".join(udp_ip)
comm = CommUDP(udp_ip, udp_port, debug=args.debug, addr_width=int(args.addr_width))
comm.open(probe=False)
comm.scan(udp_ip)
comm.close()
return
else:
print("[CommUDP] ip: {} / port: {} / ".format(udp_ip, udp_port), end="")
comm = CommUDP(udp_ip, udp_port, debug=args.debug, addr_width=int(args.addr_width))
# PCIe mode
elif args.pcie:
from litex.tools.remote.comm_pcie import CommPCIe
pcie_bar = args.pcie_bar
if pcie_bar is None:
parser.error("--pcie requires --pcie-bar.")
print("[CommPCIe] bar: {} / ".format(pcie_bar), end="")
comm = CommPCIe(pcie_bar, debug=args.debug)
# USB mode
elif args.usb:
from litex.tools.remote.comm_usb import CommUSB
if args.usb_pid is None and args.usb_vid is None:
parser.error("--usb requires --usb-vid and/or --usb-pid.")
print("[CommUSB] vid: {} / pid: {} / ".format(args.usb_vid, args.usb_pid), end="")
pid = args.usb_pid
if pid is not None:
pid = int(pid, base=0)
vid = args.usb_vid
if vid is not None:
vid = int(vid, base=0)
comm = CommUSB(vid=vid, pid=pid, max_retries=args.usb_max_retries, debug=args.debug)
# DevMem mode
elif args.devmem:
from litex.tools.remote.comm_devmem import CommDevMem
devmem_base = int(args.devmem_base, base=0)
devmem_size = int(args.devmem_size, base=0)
print("[CommDevMem] base: 0x{:08x} / size: 0x{:08x} / ".format(devmem_base, devmem_size), end="")
comm = CommDevMem(base=devmem_base, size=devmem_size, debug=args.debug)
server = RemoteServer(comm, args.bind_ip, int(args.bind_port), addr_width=int(args.addr_width))
server.open()
server.start(4)
try:
import time
while True: time.sleep(100)
except KeyboardInterrupt:
pass
if __name__ == "__main__":
main()
4443d3b3aThis file is pinned to the selected release. Download the original to open it in your design tool.