#!/usr/bin/env python3
""" hudp_setup.py : configure Hardware UDP
hudp_setup.py [opts] TXUUT RXUUT
sets up a one way transfer from TXUUT to RXUUT
Increasing spp reduces the packet rate per sample, potentially enabling a higher sample rate (do NOT exceed MTU 1400 bytes)
Increasing decimation reduces the packet rate, suitable for spp=1 low latency control, while full rate data flows to DRAM for archive
The DISCOntinuity check is a packet data checker.
Typically, the TX data comes from ACQ2106 with SPAD enabled, and the DISCO index is SPAD[0], sample ramp.
If either TXUUT or RXUUT is NOT an ACQ2106, or has already been configured for one direction, specify "none"
Examples:
Send data from UUT acq2106_363 at tx_ip ip 10.12.198.128 to UUT acq2106_364 at rx_ip=10.12.198.129::
./user_apps/acq2106/hudp_setup.py --rx_ip=10.12.198.128 --tx_ip 10.12.198.129 --run0='1 1,16,0' --play0='1 16' acq2106_363 acq2106_364
Send data from UUT acq2106_363 at tx_ip ip 10.12.198.128 to non-HUDP destination rx_ip=10.12.198.254::
./user_apps/acq2106/hudp_setup.py --rx_ip=10.12.198.254 --tx_ip 10.12.198.128 --run0='1 1,16,0' acq2106_363 none
Send data from non-HUDP source at tx_ip 10.12.198.254 to UUT acq2106_363 at rx_ip 10.12.198.128::
./user_apps/acq2106/hudp_setup.py --rx_ip=10.12.198.128 --tx_ip 10.12.198.254 --play0='1 16' none acq2106_363
In all cases,
for UUT Tx, run0 specifies data from site1 followed by a 16 column ScratchPAD.
for UUT Rx, play0 specifues datas to site1 followed by a 16 column TrashCAN.
This allows, for example a 32 channel, 16 bit ADC to play data direct to a 32 channel DAC,
including instrumentation that could be checked with --disco=16 (SPAD[0] at offset 16 LW)
::
[pgm@hoy5 acq400_hapi]$ cat /home/pgm/PROJECTS/ACQ400/ACQ420FMC/NOTES/HUDPDEMO.txt
#!/bin/bash
set -x
TX1=${TX1:-acq2106_189}
RX1=${RX1:-acq2106_274}
RX2=${RX2:-acq2106_130}
IP_TX1=${IP_TX1:-10.12.198.128}
IP_RX1=${IP_RX1:-10.12.198.129}
IP_RX2=${IP_RX2:-10.12.198.130}
echo HUDP Demo TX1:$TX1,$IP_TX1 RX1:$RX1,$IP_RX1 RX2:$RX2,$IP_RX2
echo set clk/trg
./user_apps/acq400/sync_role.py --fin=50k --fclk=50k --si5326_bypass 1 --toprole=fpmaster,strg acq2106_189
echo 'UNICAST ->' $RX1
./user_apps/acq2106/hudp_setup.py --tx_ip $IP_TX1 --rx_ip $IP_RX1 --broadcast=0 $TX1 $RX1
read continue
echo 'UNICAST ->' $RX2
./user_apps/acq2106/hudp_setup.py --tx_ip $IP_TX1 --rx_ip $IP_RX2 --broadcast=0 $TX1 $RX2
echo 'BROADCAST ->' $RX1 $RX2 and naboo
./user_apps/acq2106/hudp_setup.py --tx_ip $IP_TX1 --rx_ip $IP_RX1 --broadcast=1 $TX1 $RX1
./user_apps/acq2106/hudp_setup.py --tx_ip $IP_TX1 --rx_ip $IP_RX2 --broadcast=1 $TX1 $RX2
.. rst-class:: hidden
usage: hudp_setup.py [-h] [--netmask NETMASK] [--tx_ip TX_IP] [--rx_ip RX_IP] [--gw GW] [--port PORT] [--run0 RUN0]
[--play0 PLAY0] [--broadcast BROADCAST] [--disco DISCO] [--spp SPP]
[--hudp_decim HUDP_DECIM]
txuut rxuut
hudp_setup
positional arguments:
txuut transmit uut
rxuut transmit uut
options:
-h, --help show this help message and exit
--netmask NETMASK netmask (default: 255.255.255.0)
--tx_ip TX_IP tx ip address (default: 10.12.198.128)
--rx_ip RX_IP rx ip address (default: 10.12.198.129)
--gw GW gateway (default: 10.12.198.1)
--port PORT port (default: 53676)
--run0 RUN0 set tx sites+spad (default: 1 1,16,0)
--play0 PLAY0 set rx sites+spad (default: 1 16)
--broadcast BROADCAST broadcast the data (default: 0)
--disco DISCO enable discontinuity check at index x (default: None)
--spp SPP samples per packet (default: 1)
--hudp_decim HUDP_DECIM hudp decimation, 1..16 (default: 1)
"""
import argparse
import acq400_hapi
import time
import sys
if sys.version_info < (3, 0):
from future import builtins
from builtins import input
[docs]def hudp_init(args, uut, ip):
uut.s10.tx_ctrl = 9
uut.s10.ip = ip
uut.s10.gw = args.gw
uut.s10.netmask = args.netmask
if args.disco is not None:
print("enable disco at {}".format(args.disco))
uut.s10.disco_idx = args.disco
uut.s10.disco_en = 1
else:
uut.s10.disco_en = 0
[docs]def hudp_enable(uut):
uut.s10.tx_ctrl = 1
[docs]def init_arp_req(uut):
uut.s10.arp_request = 1
uut.s10.arp_request = 0
[docs]def ip_broadcast(args):
ip_dest = args.rx_ip.split('.')
nm = args.netmask.split('.')
for ii in range(3,0,-1):
if nm[ii] != '0':
break
else:
ip_dest[ii] = '255'
return '.'.join(ip_dest)
MTU = 9038 - 66
# tx: XI : AI, DI
[docs]def config_tx_uut(txuut, args):
print("txuut {}".format(txuut.uut))
if args.run0 != 'notouch':
txuut.s0.run0 = args.run0
hudp_init(args, txuut, args.tx_ip)
txuut.s10.hudp_decim = args.hudp_decim
txuut.s10.src_port = args.port
txuut.s10.dst_port = args.port
txuut.s10.dst_ip = args.rx_ip if args.broadcast == 0 else ip_broadcast(args)
if args.hudp_relay is not None:
txuut.s10.udp_data_src = 1
tx_ssb = int(txuut.s0.dssb) - args.hudp_relay
txuut.s10.slice_len = tx_ssb//4
txuut.s10.slice_off = args.hudp_relay//4
else:
txuut.s10.udp_data_src = 0
tx_ssb = int(txuut.s0.ssb)
txuut.s10.tx_sample_sz = tx_ssb
txuut.s10.tx_spp = args.spp
tx_pkt_sz = tx_ssb*args.spp # compute tx pkt sz and check bounds
if tx_pkt_sz > MTU:
print("ERROR packet length {} exceeds MTU {}".format(tx_pkt_sz, MTU))
hudp_enable(txuut)
init_arp_req(txuut)
tx_calc_pkt_sz = int(txuut.s10.tx_calc_pkt_sz) # actual tx pkt sz computed by FPGA logic.
if tx_pkt_sz != tx_calc_pkt_sz:
print("ERROR: set tx_pkt_size {} actual tx_pkt_size {}".format(tx_pkt_sz, tx_calc_pkt_sz))
print("TX configured. ssb:{} spp:{} tx_pkt_size {}".format(tx_ssb, args.spp, tx_pkt_sz))
# rx: XO : AO, DO
[docs]def config_rx_uut(rxuut, args):
print("rxuut {}".format(rxuut.uut))
if args.play0 != 'notouch':
rxuut.s0.play0 = args.play0
rxuut.s0.distributor = 'comms=U off'
rxuut.s0.distributor = 'on'
hudp_init(args, rxuut, args.rx_ip)
rxuut.s10.rx_src_ip = args.tx_ip
rxuut.s10.rx_port = args.port
hudp_enable(rxuut)
PCSEL = ("none", "pc")
[docs]def run_main(args):
if args.gw is None:
args.gw = args.rx_ip
if args.txuut[0] not in PCSEL:
config_tx_uut(acq400_hapi.factory(args.txuut[0]), args)
if args.rxuut[0] not in PCSEL:
config_rx_uut(acq400_hapi.factory(args.rxuut[0]), args)
[docs]def get_parser():
parser = argparse.ArgumentParser(description="Setup HUDP for UUTs",
formatter_class=argparse.ArgumentDefaultsHelpFormatter)
parser.add_argument("--netmask", default='255.255.255.0', help='netmask')
parser.add_argument("--tx_ip", default='10.12.198.128', help='tx ip address')
parser.add_argument("--rx_ip", default='10.12.198.129', help='rx ip address')
parser.add_argument("--gw", default=None, help='gateway')
parser.add_argument("--port", default='53676', help='port')
parser.add_argument("--run0", default='1 1,16,0', help="set tx sites+spad or notouch if set elsewhere")
parser.add_argument("--play0", default='1 16', help="set rx sites+spad or notouch if set elsewhere")
parser.add_argument("--broadcast", default=0, type = int, help="broadcast the data")
parser.add_argument("--disco", default=None, type=int, help="enable discontinuity check at index x")
parser.add_argument("--hudp_relay", default=None, type=int, help="0..N: relay LLC VI out HUDP txt offset in vector")
parser.add_argument("--spp", default=1, type=int, help="samples per packet")
parser.add_argument("--hudp_decim", default=1, type=int, help="hudp decimation, 1..16")
parser.add_argument("txuut", nargs=1, help=f"transmit uut (if it's a PC, type one of {PCSEL})")
parser.add_argument("rxuut", nargs=1, help=f"rx uut (if it's a PC, type one of {PCSEL})")
return parser
if __name__ == '__main__':
run_main(get_parser().parse_args())