aboutsummaryrefslogtreecommitdiffstats
path: root/mpm/python/usrp_mpm
diff options
context:
space:
mode:
Diffstat (limited to 'mpm/python/usrp_mpm')
-rw-r--r--mpm/python/usrp_mpm/CMakeLists.txt2
-rw-r--r--mpm/python/usrp_mpm/dboard_manager/neon.py4
-rw-r--r--mpm/python/usrp_mpm/dboard_manager/rhodium.py1
-rw-r--r--mpm/python/usrp_mpm/ethdispatch.py102
-rw-r--r--mpm/python/usrp_mpm/ethtable.py187
-rw-r--r--mpm/python/usrp_mpm/periph_manager/base.py264
-rw-r--r--mpm/python/usrp_mpm/periph_manager/e31x.py154
-rw-r--r--mpm/python/usrp_mpm/periph_manager/e31x_periphs.py166
-rw-r--r--mpm/python/usrp_mpm/periph_manager/e320.py188
-rw-r--r--mpm/python/usrp_mpm/periph_manager/e320_periphs.py164
-rw-r--r--mpm/python/usrp_mpm/periph_manager/n3xx.py199
-rw-r--r--mpm/python/usrp_mpm/periph_manager/n3xx_periphs.py67
-rw-r--r--mpm/python/usrp_mpm/rpc_server.py2
-rw-r--r--mpm/python/usrp_mpm/xports/xportmgr_liberio.py69
-rw-r--r--mpm/python/usrp_mpm/xports/xportmgr_udp.py218
15 files changed, 957 insertions, 830 deletions
diff --git a/mpm/python/usrp_mpm/CMakeLists.txt b/mpm/python/usrp_mpm/CMakeLists.txt
index 6b6d99a30..e4332a617 100644
--- a/mpm/python/usrp_mpm/CMakeLists.txt
+++ b/mpm/python/usrp_mpm/CMakeLists.txt
@@ -16,7 +16,7 @@ set(USRP_MPM_TOP_FILES
${CMAKE_CURRENT_SOURCE_DIR}/discovery.py
${CMAKE_CURRENT_SOURCE_DIR}/eeprom.py
${CMAKE_CURRENT_SOURCE_DIR}/e31x_legacy_eeprom.py
- ${CMAKE_CURRENT_SOURCE_DIR}/ethtable.py
+ ${CMAKE_CURRENT_SOURCE_DIR}/ethdispatch.py
${CMAKE_CURRENT_SOURCE_DIR}/gpsd_iface.py
${CMAKE_CURRENT_SOURCE_DIR}/liberiotable.py
${CMAKE_CURRENT_SOURCE_DIR}/mpmlog.py
diff --git a/mpm/python/usrp_mpm/dboard_manager/neon.py b/mpm/python/usrp_mpm/dboard_manager/neon.py
index 00438503d..7f9abd5ce 100644
--- a/mpm/python/usrp_mpm/dboard_manager/neon.py
+++ b/mpm/python/usrp_mpm/dboard_manager/neon.py
@@ -286,8 +286,8 @@ class Neon(DboardManagerBase):
return {
'name': 'ad9361_lock',
'type': 'BOOLEAN',
- 'unit': 'locked' if lo_locked else 'unlocked',
- 'value': str(lo_locked).lower(),
+ 'unit': 'locked' if lo_locked else 'unlocked',
+ 'value': str(lo_locked).lower(),
}
def get_catalina_temp_sensor(self, _):
diff --git a/mpm/python/usrp_mpm/dboard_manager/rhodium.py b/mpm/python/usrp_mpm/dboard_manager/rhodium.py
index b53b7f5bb..85f7b2b60 100644
--- a/mpm/python/usrp_mpm/dboard_manager/rhodium.py
+++ b/mpm/python/usrp_mpm/dboard_manager/rhodium.py
@@ -20,7 +20,6 @@ from usrp_mpm.dboard_manager.adc_rh import AD9695Rh
from usrp_mpm.dboard_manager.dac_rh import DAC37J82Rh
from usrp_mpm.mpmlog import get_logger
from usrp_mpm.sys_utils.uio import open_uio
-from usrp_mpm.sys_utils.udev import get_eeprom_paths
from usrp_mpm.user_eeprom import BfrfsEEPROM
diff --git a/mpm/python/usrp_mpm/ethdispatch.py b/mpm/python/usrp_mpm/ethdispatch.py
new file mode 100644
index 000000000..7c7437225
--- /dev/null
+++ b/mpm/python/usrp_mpm/ethdispatch.py
@@ -0,0 +1,102 @@
+#
+# Copyright 2017-2018 Ettus Research, a National Instruments Company
+#
+# SPDX-License-Identifier: GPL-3.0-or-later
+#
+"""
+Ethernet dispatcher table control
+"""
+
+from builtins import str
+from builtins import object
+import netaddr
+from usrp_mpm.mpmlog import get_logger
+from usrp_mpm.sys_utils.uio import UIO
+
+
+class EthDispatcherCtrl(object):
+ """
+ Controls an Ethernet dispatcher.
+ """
+ DEFAULT_VITA_PORT = (49153, 49154)
+ # Address offsets:
+ ETH_IP_OFFSET = 0x0000
+ ETH_PORT_OFFSET = 0x0004
+ FORWARD_ETH_BCAST_OFFSET = 0x0008
+ BRIDGE_MAC_LO_OFFSET = 0x0010
+ BRIDGE_MAC_HI_OFFSET = 0x0014
+ BRIDGE_IP_OFFSET = 0x0018
+ BRIDGE_PORT_OFFSET = 0x001c
+ BRIDGE_ENABLE_OFFSET = 0x0020
+
+
+ def __init__(self, label):
+ self.log = get_logger(label)
+ self._regs = UIO(label=label, read_only=False)
+ self.poke32 = self._regs.poke32
+ self.peek32 = self._regs.peek32
+
+ def set_bridge_mode(self, bridge_mode):
+ " Enable/Disable Bridge Mode "
+ self.log.trace("Bridge Mode {}".format(
+ "Enabled" if bridge_mode else "Disabled"
+ ))
+ self.poke32(self.BRIDGE_ENABLE_OFFSET, int(bridge_mode))
+
+ def set_bridge_mac_addr(self, mac_addr):
+ """
+ Set the bridge MAC address for this Ethernet dispatcher.
+ Outgoing packets will have this MAC address.
+ """
+ self.log.debug("Setting bridge MAC address to `{}'".format(mac_addr))
+ mac_addr_int = int(netaddr.EUI(mac_addr))
+ self.log.trace("Writing to address 0x{:04X}: 0x{:04X}".format(
+ self.BRIDGE_MAC_LO_OFFSET, mac_addr_int & 0xFFFFFFFF
+ ))
+ self.poke32(self.BRIDGE_MAC_LO_OFFSET, mac_addr_int & 0xFFFFFFFF)
+ self.log.trace("Writing to address 0x{:04X}: 0x{:04X}".format(
+ self.BRIDGE_MAC_HI_OFFSET, mac_addr_int >> 32
+ ))
+ self.poke32(self.BRIDGE_MAC_HI_OFFSET, mac_addr_int >> 32)
+
+ def set_ipv4_addr(self, ip_addr, bridge_en=False):
+ """
+ Set the own IPv4 address for this Ethernet dispatcher.
+ Outgoing packets will have this IP address.
+ """
+ if bridge_en:
+ own_ip_offset = self.BRIDGE_IP_OFFSET
+ else:
+ own_ip_offset = self.ETH_IP_OFFSET
+ self.log.debug("Setting my own IP address to `{}'".format(ip_addr))
+ ip_addr_int = int(netaddr.IPAddress(ip_addr))
+ with self._regs:
+ self.poke32(own_ip_offset, ip_addr_int)
+
+ def set_vita_port(self, port_value=None, port_idx=None, bridge_en=False):
+ """
+ Set the port that is used for incoming VITA traffic. This is used to
+ distinguish traffic that goes to the FPGA from that going to the ARM.
+ """
+ port_idx = port_idx or 0
+ port_value = port_value or self.DEFAULT_VITA_PORT[port_idx]
+ assert port_idx in (0,) #FIXME: Fix port_idx = 1
+ if bridge_en:
+ port_reg_addr = self.BRIDGE_PORT_OFFSET
+ else:
+ port_reg_addr = self.ETH_PORT_OFFSET
+ with self._regs:
+ self.poke32(port_reg_addr, port_value)
+ self.log.debug("Setting RFNOC UDP port to `{}'".format(port_value))
+
+ def set_forward_policy(self, forward_eth, forward_bcast):
+ """
+ Forward Ethernet packet not matching OWN_IP to CROSSOVER
+ Forward broadcast packet to CPU and CROSSOVER
+ """
+ reg_value = int(bool(forward_eth) << 1) | int(bool(forward_bcast))
+ self.log.trace("Writing to address 0x{:04X}: 0x{:04X}".format(
+ self.FORWARD_ETH_BCAST_OFFSET, reg_value
+ ))
+ with self._regs:
+ self.poke32(self.FORWARD_ETH_BCAST_OFFSET, reg_value)
diff --git a/mpm/python/usrp_mpm/ethtable.py b/mpm/python/usrp_mpm/ethtable.py
deleted file mode 100644
index 4a6566987..000000000
--- a/mpm/python/usrp_mpm/ethtable.py
+++ /dev/null
@@ -1,187 +0,0 @@
-#
-# Copyright 2017-2018 Ettus Research, a National Instruments Company
-#
-# SPDX-License-Identifier: GPL-3.0-or-later
-#
-"""
-Ethernet dispatcher table control
-"""
-
-from builtins import str
-from builtins import object
-import netaddr
-from usrp_mpm.mpmlog import get_logger
-from usrp_mpm.sys_utils.uio import UIO
-from usrp_mpm.sys_utils.net import get_mac_addr
-
-
-class EthDispatcherTable(object):
- """
- Controls an Ethernet dispatcher table.
- """
- DEFAULT_VITA_PORT = (49153, 49154)
- # Address offsets:
- ETH_IP_OFFSET = 0x0000
- ETH_PORT_OFFSET = 0x0004
- FORWARD_ETH_BCAST_OFFSET = 0x0008
- BRIDGE_MAC_LO_OFFSET = 0x0010
- BRIDGE_MAC_HI_OFFSET = 0x0014
- BRIDGE_IP_OFFSET = 0x0018
- BRIDGE_PORT_OFFSET = 0x001c
- BRIDGE_ENABLE_OFFSET = 0x0020
- SID_IP_OFFSET = 0x1000
- SID_PORT_MAC_HI_OFFSET = 0x1400
- SID_MAC_LO_OFFSET = 0x1800
- LADDR_IP_OFFSET = 0x1D00
- LADDR_PORT_MAC_HI_OFFSET = 0x1E00
- LADDR_MAC_LO_OFFSET = 0x1F00
-
-
- def __init__(self, label):
- self.log = get_logger(label)
- self._regs = UIO(label=label, read_only=False)
- self.poke32 = self._regs.poke32
- self.peek32 = self._regs.peek32
-
- def set_bridge_mode(self, bridge_mode):
- " Enable/Disable Bridge Mode "
- self.log.trace("Bridge Mode {}".format(
- "Enabled" if bridge_mode else "Disabled"
- ))
- self.poke32(self.BRIDGE_ENABLE_OFFSET, int(bridge_mode))
-
- def set_bridge_mac_addr(self, mac_addr):
- """
- Set the bridge MAC address for this Ethernet dispatcher.
- Outgoing packets will have this MAC address.
- """
- self.log.debug("Setting bridge MAC address to `{}'".format(mac_addr))
- mac_addr_int = int(netaddr.EUI(mac_addr))
- self.log.trace("Writing to address 0x{:04X}: 0x{:04X}".format(
- self.BRIDGE_MAC_LO_OFFSET, mac_addr_int & 0xFFFFFFFF
- ))
- self.poke32(self.BRIDGE_MAC_LO_OFFSET, mac_addr_int & 0xFFFFFFFF)
- self.log.trace("Writing to address 0x{:04X}: 0x{:04X}".format(
- self.BRIDGE_MAC_HI_OFFSET, mac_addr_int >> 32
- ))
- self.poke32(self.BRIDGE_MAC_HI_OFFSET, mac_addr_int >> 32)
-
- def set_ipv4_addr(self, ip_addr, bridge_en=False):
- """
- Set the own IPv4 address for this Ethernet dispatcher.
- Outgoing packets will have this IP address.
- """
- if bridge_en:
- own_ip_offset = self.BRIDGE_IP_OFFSET
- else:
- own_ip_offset = self.ETH_IP_OFFSET
- self.log.debug("Setting my own IP address to `{}'".format(ip_addr))
- ip_addr_int = int(netaddr.IPAddress(ip_addr))
- with self._regs:
- self.poke32(own_ip_offset, ip_addr_int)
-
- def set_vita_port(self, port_value=None, port_idx=None, bridge_en=False):
- """
- Set the port that is used for incoming VITA traffic. This is used to
- distinguish traffic that goes to the FPGA from that going to the ARM.
- """
- port_idx = port_idx or 0
- port_value = port_value or self.DEFAULT_VITA_PORT[port_idx]
- assert port_idx in (0) #FIXME: Fix port_idx = 1
- if bridge_en:
- port_reg_addr = self.BRIDGE_PORT_OFFSET
- else:
- port_reg_addr = self.ETH_PORT_OFFSET
- with self._regs:
- self.poke32(port_reg_addr, port_value)
-
- def set_route(self, sid, ip_addr, udp_port, mac_addr=None):
- """
- Sets up routing in the Ethernet dispatcher. From sid, only the
- destination part is important. After this call, any CHDR packet
- reaching this Ethernet dispatcher will get routed to `ip_addr' and
- `udp_port'.
-
- It automatically looks up the MAC address of the destination unless a
- MAC address is given.
-
- sid -- Full SID, but only destination part matters.
- ip_addr -- IPv4 destination address. String format ("1.2.3.4").
- udp_addr -- Destination UDP port.
- mac_addr -- If given, this will replace an ARP lookup for the MAC
- address. String format, ("aa:bb:cc:dd:ee:ff"), case does
- not matter.
- """
- udp_port = int(udp_port)
- if mac_addr is None:
- mac_addr = get_mac_addr(ip_addr)
- if mac_addr is None:
- self.log.error(
- "Could not resolve a MAC address for IP address `{}'".format(ip_addr)
- )
- if sid.dst_addr == 0 or sid.dst_addr == 1:
- table_addr = sid.dst_ep
- ip_base_offset = self.SID_IP_OFFSET
- port_mac_hi_base_offset = self.SID_PORT_MAC_HI_OFFSET
- mac_lo_base_offset = self.SID_MAC_LO_OFFSET
- self.log.debug(
- "Routing SID `{sid}' (endpoint `{ep}') to IP address `{ip}', " \
- "MAC address `{mac}', port `{port}'".format(
- sid=str(sid),
- ep=table_addr,
- ip=ip_addr,
- mac=mac_addr,
- port=udp_port
- )
- )
- else: #populate local addr eth dispatch table
- table_addr = sid.dst_addr
- ip_base_offset = self.LADDR_IP_OFFSET
- port_mac_hi_base_offset = self.LADDR_PORT_MAC_HI_OFFSET
- mac_lo_base_offset = self.LADDR_MAC_LO_OFFSET
- self.log.debug(
- "Routing SID `{sid}' (local addr`{addr}') to IP address `{ip}', " \
- "MAC address `{mac}', port `{port}'".format(
- sid=str(sid),
- addr=table_addr,
- ip=ip_addr,
- mac=mac_addr,
- port=udp_port
- )
- )
- ip_addr_int = int(netaddr.IPAddress(ip_addr))
- mac_addr_int = int(netaddr.EUI(mac_addr))
- dst_offset = 4 * table_addr
-
- def poke_and_trace(addr, data):
- " Do a poke32() and log.trace() "
- self.log.trace("Writing to address 0x{:04X}: 0x{:04X}".format(
- addr, data
- ))
- self.poke32(addr, data)
-
- with self._regs:
- poke_and_trace(
- ip_base_offset + dst_offset,
- ip_addr_int
- )
- poke_and_trace(
- mac_lo_base_offset + dst_offset,
- mac_addr_int & 0xFFFFFFFF,
- )
- poke_and_trace(
- port_mac_hi_base_offset + dst_offset,
- (udp_port << 16) | (mac_addr_int >> 32)
- )
-
- def set_forward_policy(self, forward_eth, forward_bcast):
- """
- Forward Ethernet packet not matching OWN_IP to CROSSOVER
- Forward broadcast packet to CPU and CROSSOVER
- """
- reg_value = int(bool(forward_eth) << 1) | int(bool(forward_bcast))
- self.log.trace("Writing to address 0x{:04X}: 0x{:04X}".format(
- self.FORWARD_ETH_BCAST_OFFSET, reg_value
- ))
- with self._regs:
- self.poke32(self.FORWARD_ETH_BCAST_OFFSET, reg_value)
diff --git a/mpm/python/usrp_mpm/periph_manager/base.py b/mpm/python/usrp_mpm/periph_manager/base.py
index be7600333..4ae30778a 100644
--- a/mpm/python/usrp_mpm/periph_manager/base.py
+++ b/mpm/python/usrp_mpm/periph_manager/base.py
@@ -177,7 +177,7 @@ class PeriphManagerBase(object):
###########################################################################
def __init__(self):
# Note: args is a dictionary.
- assert len(self.pids) > 0
+ assert self.pids
assert self.mboard_eeprom_magic is not None
self.dboards = []
self._default_args = ""
@@ -241,14 +241,17 @@ class PeriphManagerBase(object):
If no EEPROM is defined, returns empty values.
"""
- if len(self.mboard_eeprom_addr):
+ if self.mboard_eeprom_addr:
self.log.trace("Reading EEPROM from address `{}'..."
.format(self.mboard_eeprom_addr))
- if (not get_eeprom_paths(self.mboard_eeprom_addr)):
- raise RuntimeError("No EEPROM found at address `{}'"
- .format(self.mboard_eeprom_addr))
+ eeprom_paths = get_eeprom_paths(self.mboard_eeprom_addr)
+ if not eeprom_paths:
+ self.log.error("Could not identify EEPROM paths for %s!",
+ self.mboard_eeprom_addr)
+ return {}, b''
+ self.log.trace("Found mboard EEPROM path: %s", eeprom_paths[0])
(eeprom_head, eeprom_rawdata) = eeprom.read_eeprom(
- get_eeprom_paths(self.mboard_eeprom_addr)[0],
+ eeprom_paths[0],
self.mboard_eeprom_offset,
eeprom.MboardEEPROM.eeprom_header_format,
eeprom.MboardEEPROM.eeprom_header_keys,
@@ -512,6 +515,22 @@ class PeriphManagerBase(object):
###########################################################################
# Misc device status controls and indicators
###########################################################################
+ def set_device_id(self, device_id):
+ """
+ Sets the device ID for this motherboard.
+ The device ID is used to identify the RFNoC components associated with
+ this motherboard.
+ """
+ raise NotImplementedError("set_device_id() not implemented.")
+
+ def get_device_id(self):
+ """
+ Gets the device ID for this motherboard.
+ The device ID is used to identify the RFNoC components associated with
+ this motherboard.
+ """
+ raise NotImplementedError("get_device_id() not implemented.")
+
def get_init_status(self):
"""
Returns the status of the device after its initialization (that happens
@@ -592,6 +611,20 @@ class PeriphManagerBase(object):
"""
return [dboard.device_info for dboard in self.dboards]
+ @no_claim
+ def get_proto_ver(self):
+ """
+ Return RFNoC protocol version
+ """
+ raise NotImplementedError("get_proto_ver() not implemented.")
+
+ @no_claim
+ def get_chdr_width(self):
+ """
+ Return RFNoC CHDR width
+ """
+ raise NotImplementedError("get_chdr_width() not implemented.")
+
###########################################################################
# Component updating
###########################################################################
@@ -670,64 +703,10 @@ class PeriphManagerBase(object):
self.log.trace("Component info: {}".format(metadata))
# Convert all values to str
return dict([a, str(x)] for a, x in metadata.items())
- else:
- self.log.trace("Component not found in updateable components: {}"
- .format(component_name))
- return {}
-
- ###########################################################################
- # Crossbar control
- ###########################################################################
- @no_claim
- def get_num_xbars(self):
- """
- Returns the number of crossbars instantiated in the current design
- """
- return 1 # FIXME
-
- @no_claim
- def get_num_blocks(self, xbar_index):
- """
- Returns the number of blocks connected to crossbar with index
- xbar_index.
-
- xbar_index -- The index of the crossbar that's being queried.
- docstring for get_num_blocks"""
- # FIXME udev lookup
- xbar_sysfs_path = '/sys/class/rfnoc_crossbar/crossbar{}/nports'.format(
- xbar_index
- )
- return int(open(xbar_sysfs_path).read().strip()) - \
- self.get_base_port(xbar_index)
-
- @no_claim
- def get_base_port(self, xbar_index):
- """
- Returns the index of the first port which is connected to an RFNoC
- block. Example: Assume there are two SFPs connected to the crossbar, and
- one DMA engine for CHDR traffic. The convention would be to connect
- those to ports 0, 1, and 2, respectively. This makes port 3 the first
- block to be connected to an RFNoC block.
-
- xbar_index -- The index of the crossbar that's being queried
- """
- return self.crossbar_base_port
-
- def set_xbar_local_addr(self, xbar_index, local_addr):
- """
- Program crossbar xbar_index to have the local address local_addr.
- """
- # FIXME udev lookup
- xbar_sysfs_path = '/sys/class/rfnoc_crossbar/crossbar{}/local_addr'.format(
- xbar_index
- )
- laddr_value = "0x{:X}".format(local_addr)
- self.log.trace("Setting local address for xbar {} to {}.".format(
- xbar_sysfs_path, laddr_value
- ))
- with open(xbar_sysfs_path, "w") as xbar_file:
- xbar_file.write(laddr_value)
- return True
+ # else:
+ self.log.trace("Component not found in updateable components: {}"
+ .format(component_name))
+ return {}
##########################################################################
# Mboard Sensors
@@ -823,86 +802,44 @@ class PeriphManagerBase(object):
#######################################################################
# Transport API
#######################################################################
- def request_xport(
- self,
- dst_address,
- suggested_src_address,
- xport_type,
- ):
- """
- When setting up a CHDR connection, this is the first call to be
- made. This function will return a list of dictionaries, each
- describing a way to open an CHDR connection. The list of dictionaries
- is sorted by preference, meaning that the caller should use the first
- option, if possible.
- All transports requested are bidirectional.
-
- The callee must maintain a lock on the available CHDR xports. After
- calling request_xport(), the caller needs to pick one of the
- dictionaries, possibly amend data (e.g., if the connection is an
- Ethernet connection, then we need to know the source port, but more
- details on that in commit_xport()'s documentation).
- One way to implement a lock is to simply lock a mutex here and
- unlock it in commit_xport(), even though there are probably more
- nuanced solutions.
+ def get_chdr_link_types(self):
+ """
+ Return a list of ways how the UHD session can connect to this device to
+ initiate CHDR traffic.
- Arguments:
- dst_sid -- The destination part of the connection, i.e., which
- RFNoC block are we connecting to. Example: 0x0230
- suggested_src_sid -- The source part of the connection, i.e.,
- what's the source address of packets going to
- the destination at dst_sid. This is a
- suggestion, MPM can override this. Example:
- 0x0001.
- xport_type -- One of the following strings: CTRL, ASYNC_MSG,
- TX_DATA, RX_DATA. See also xports_type_t in UHD.
-
- The return value is a list of dictionaries. Every dictionary has
- the following key/value pairs:
- - type: Type of transport, e.g., "UDP", "liberio".
- - ipv4 (UDP only): IPv4 address to connect to.
- - port (UDP only): IP port to connect to.
- - send_sid: String version of the SID used for this transport. This is
- the definitive version of the SID, the suggested_src_address
- can be ignored at this point.
- - allocation: This is an integer value which represents a score of
- how much bandwidth is used. Note: Currently does not
- have any unit, is just a counter. Higher numbers mean
- higher utilization. RX means device to UHD, for
- example, committing an RX streamer would increase this
- value.
- This key is optional, MPM does not have to provide it.
- If the allocation affects the preference, it will be
- factored into the order of the results, meaning the
- caller does not strictly have to check its value even if
- the transport option with the smallest allocation is
- preferred.
-
- Note: The dictionary may include other keys which should be ignored,
- or at the very least, kept intact. commit_xport() might be requiring
- them.
- """
- raise NotImplementedError("request_xport() not implemented.")
-
- def commit_xport(self, xport_info):
- """
- When setting up a CHDR connection, this is the second call to be
- made.
+ The return value is a list of strings. Every string is a key for a
+ transport type. Values include:
+ - "udp": Means this device can be reached via UDP
+ - "liberio": Means this device can be reached via Liberio (local DMA)
- Arguments:
- xport_info -- A dictionary (string -> string). The dictionary must
- have been originally created by request_xport(), but
- additional key/value pairs need to be added.
+ The list is filtered based on what the device knows about where the UHD
+ session is. For example, on an N310, it will only either return "UDP"
+ or "Liberio", depending on if we're remotely launching UHD, or locally.
- All transports need to also provide:
- - rx_mtu: In bytes, the max number of bytes going from device to UHD
- - tx_mtu: In bytes, the max number of bytes going from UHD to device
+ In order to get further information about how to connect to the device,
+ the keys returned from this function can be used with
+ get_chdr_link_options().
+ """
+ raise NotImplementedError("get_chdr_link_types() not implemented.")
- UDP transports need to also provide:
- - src_ipv4: IPv4 address the connection is coming from.
- - src_port: IP port the connection is coming from.
+ def get_chdr_link_options(self, xport_type):
"""
- raise NotImplementedError("commit_xport() not implemented.")
+ Returns a list of dictionaries. Every dictionary contains information
+ about one way to connect to this device in order to initiate CHDR
+ traffic.
+
+ The interpretation of the return value is very highly dependant on the
+ transport type (xport_type).
+ For UDP, the every entry of the list has the following keys:
+ - ipv4 (IP Address)
+ - port (UDP port)
+ - link_rate (bps of the link, e.g. 10e9 for 10GigE)
+
+ For Liberio, every entry has the following keys:
+ - tx_dev: TX device (/dev/tx-dma*)
+ - rx_dev: RX device (/dev/rx-dma*)
+ """
+ raise NotImplementedError("get_chdr_link_options() not implemented.")
#######################################################################
# Claimer API
@@ -927,3 +864,52 @@ class PeriphManagerBase(object):
"""
self.log.debug("Device was unclaimed. No actions defined.")
+ #######################################################################
+ # Timekeeper API
+ #######################################################################
+ def get_num_timekeepers(self):
+ """
+ Return the number of timekeepers
+ """
+ raise NotImplementedError("get_num_timekeepers() not implemented.")
+
+ def get_timekeeper_time(self, tk_idx, last_pps):
+ """
+ Get the time in ticks
+
+ Arguments:
+ tk_idx: Index of timekeeper
+ next_pps: If True, get time at last PPS. Otherwise, get time now.
+ """
+ raise NotImplementedError(
+ "get_ticks_now({}, {}) not implemented.".format(tk_idx, last_pps))
+
+ def set_timekeeper_time(self, tk_idx, ticks, next_pps):
+ """
+ Set the time in ticks
+
+ Arguments:
+ tk_idx: Index of timekeeper
+ ticks: Time in ticks
+ next_pps: If True, set time at next PPS. Otherwise, set time now.
+ """
+ raise NotImplementedError(
+ "set_ticks_last_pps({}, {}, {}) not implemented."
+ .format(tk_idx, ticks, next_pps))
+
+ def set_tick_period(self, tk_idx, period_ns):
+ """
+ Set the time per tick in nanoseconds (tick period)
+
+ Arguments:
+ tk_idx: Index of timekeeper
+ period_ns: Period in nanoseconds
+ """
+ raise NotImplementedError(
+ "set_tick_period({}) not implemented.".format(tk_idx, period_ns))
+
+ def get_clocks(self):
+ """
+ Gets the RFNoC-related clocks present in the FPGA design
+ """
+ raise NotImplementedError("get_clocks() not implemented.")
diff --git a/mpm/python/usrp_mpm/periph_manager/e31x.py b/mpm/python/usrp_mpm/periph_manager/e31x.py
index 722f8d7a9..e93cbef25 100644
--- a/mpm/python/usrp_mpm/periph_manager/e31x.py
+++ b/mpm/python/usrp_mpm/periph_manager/e31x.py
@@ -30,7 +30,7 @@ from usrp_mpm import e31x_legacy_eeprom
E310_DEFAULT_CLOCK_SOURCE = 'internal'
E310_DEFAULT_TIME_SOURCE = 'internal'
E310_DEFAULT_ENABLE_FPGPIO = True
-E310_FPGA_COMPAT = (1,0)
+E310_FPGA_COMPAT = (4,0)
E310_DBOARD_SLOT_IDX = 0
###############################################################################
@@ -39,9 +39,7 @@ E310_DBOARD_SLOT_IDX = 0
class E310XportMgrLiberio(XportMgrLiberio):
" E310-specific Liberio configuration "
- max_chan = 4
- xbar_dev = "/dev/crossbar0"
- xbar_port = 0
+ max_chan = 5
###############################################################################
# Main Class
@@ -178,7 +176,6 @@ class e31x(ZynqComponents, PeriphManagerBase):
self._tear_down = False
self._clock_source = None
self._time_source = None
- self._available_endpoints = list(range(256))
self.dboard = self.dboards[E310_DBOARD_SLOT_IDX]
try:
self._init_peripherals(self.args_cached)
@@ -277,9 +274,6 @@ class e31x(ZynqComponents, PeriphManagerBase):
self.mboard_regs_control.get_build_timestamp()
self._check_fpga_compat()
self._update_fpga_type()
- self.crossbar_base_port = self.mboard_regs_control.get_xbar_baseport()
- self.log.debug("crossbar base port: {}".format(self.crossbar_base_port))
-
# Init clocking
self._init_ref_clock_and_time(args)
# Init CHDR transports
@@ -443,7 +437,6 @@ class e31x(ZynqComponents, PeriphManagerBase):
for xport_mgr in itervalues(self._xport_mgrs):
xport_mgr.deinit()
self.log.trace("Resetting SID pool...")
- self._available_endpoints = list(range(256))
if not self._do_not_reload:
self.tear_down()
# Reset back to value from _default_args (mpm.conf)
@@ -480,56 +473,22 @@ class e31x(ZynqComponents, PeriphManagerBase):
self.log.trace("Found idle overlay: %s", idle_overlay)
return is_idle
+
###########################################################################
# Transport API
###########################################################################
- def request_xport(
- self,
- dst_address,
- suggested_src_address,
- xport_type
- ):
- """
- See PeriphManagerBase.request_xport() for docs.
- """
- # Try suggested address first, then just pick the first available one:
- src_address = suggested_src_address
- if src_address not in self._available_endpoints:
- if not self._available_endpoints:
- raise RuntimeError(
- "Depleted pool of SID endpoints for this device!")
- else:
- src_address = self._available_endpoints[0]
- sid = SID(src_address << 16 | dst_address)
- # Note: This SID may change its source address!
- self.log.trace(
- "request_xport(dst=0x%04X, suggested_src_address=0x%04X, xport_type=%s): " \
- "operating on temporary SID: %s",
- dst_address, suggested_src_address, str(xport_type), str(sid))
- assert self.mboard_info['rpc_connection'] in ('local')
- if self.mboard_info['rpc_connection'] == 'local':
- return self._xport_mgrs['liberio'].request_xport(
- sid,
- xport_type,
- )
-
- def commit_xport(self, xport_info):
+ def get_chdr_link_types(self):
"""
- See PeriphManagerBase.commit_xport() for docs.
+ See PeriphManagerBase.get_chdr_link_types() for docs.
+ """
+ return ['liberio']
- Reminder: All connections are incoming, i.e. "send" or "TX" means
- remote device to local device, and "receive" or "RX" means this local
- device to remote device. "Remote device" can be, for example, a UHD
- session.
+ def get_chdr_link_options(self, xport_type):
+ """
+ See PeriphManagerBase.get_chdr_link_options() for docs.
"""
- ## Go, go, go
- assert self.mboard_info['rpc_connection'] in ('local')
- sid = SID(xport_info['send_sid'])
- self._available_endpoints.remove(sid.src_ep)
- self.log.debug("Committing transport for SID %s, xport info: %s",
- str(sid), str(xport_info))
- if self.mboard_info['rpc_connection'] == 'local':
- return self._xport_mgrs['liberio'].commit_xport(sid, xport_info)
+ if xport_type == 'liberio':
+ return self._xport_mgrs['liberio'].get_chdr_link_options()
###########################################################################
# Device info
@@ -550,6 +509,39 @@ class e31x(ZynqComponents, PeriphManagerBase):
})
return device_info
+ def set_device_id(self, device_id):
+ """
+ Sets the device ID for this motherboard.
+ The device ID is used to identify the RFNoC components associated with
+ this motherboard.
+ """
+ self.log.debug("Setting device ID to `{}'".format(device_id))
+ self.mboard_regs_control.set_device_id(device_id)
+
+ def get_device_id(self):
+ """
+ Gets the device ID for this motherboard.
+ The device ID is used to identify the RFNoC components associated with
+ this motherboard.
+ """
+ return self.mboard_regs_control.get_device_id()
+
+ def get_proto_ver(self):
+ """
+ Return RFNoC protocol version
+ """
+ proto_ver = self.mboard_regs_control.get_proto_ver()
+ self.log.debug("RFNoC protocol version supported by this device is {}".format(proto_ver))
+ return proto_ver
+
+ def get_chdr_width(self):
+ """
+ Return RFNoC CHDR width
+ """
+ chdr_width = self.mboard_regs_control.get_chdr_width()
+ self.log.debug("CHDR width supported by the device is {}".format(chdr_width))
+ return chdr_width
+
###########################################################################
# Clock/Time API
###########################################################################
@@ -742,3 +734,59 @@ class e31x(ZynqComponents, PeriphManagerBase):
fpga_type = self.mboard_regs_control.get_fpga_type()
self.log.debug("Updating mboard FPGA type info to {}".format(fpga_type))
self.updateable_components['fpga']['type'] = fpga_type
+
+ #######################################################################
+ # Timekeeper API
+ #######################################################################
+ def get_num_timekeepers(self):
+ """
+ Return the number of timekeepers
+ """
+ return self.mboard_regs_control.get_num_timekeepers()
+
+ def get_timekeeper_time(self, tk_idx, last_pps):
+ """
+ Get the time in ticks
+
+ Arguments:
+ tk_idx: Index of timekeeper
+ next_pps: If True, get time at last PPS. Otherwise, get time now.
+ """
+ return self.mboard_regs_control.get_timekeeper_time(tk_idx, last_pps)
+
+ def set_timekeeper_time(self, tk_idx, ticks, next_pps):
+ """
+ Set the time in ticks
+
+ Arguments:
+ tk_idx: Index of timekeeper
+ ticks: Time in ticks
+ next_pps: If True, set time at next PPS. Otherwise, set time now.
+ """
+ self.mboard_regs_control.set_timekeeper_time(tk_idx, ticks, next_pps)
+
+ def set_tick_period(self, tk_idx, period_ns):
+ """
+ Set the time per tick in nanoseconds (tick period)
+
+ Arguments:
+ tk_idx: Index of timekeeper
+ period_ns: Period in nanoseconds
+ """
+ self.mboard_regs_control.set_tick_period(tk_idx, period_ns)
+
+ def get_clocks(self):
+ """
+ Gets the RFNoC-related clocks present in the FPGA design
+ """
+ return [
+ {
+ 'name': 'radio_clk',
+ 'freq': str(self.dboard.get_master_clock_rate()),
+ 'mutable': 'true'
+ },
+ {
+ 'name': 'bus_clk',
+ 'freq': str(100e6),
+ }
+ ]
diff --git a/mpm/python/usrp_mpm/periph_manager/e31x_periphs.py b/mpm/python/usrp_mpm/periph_manager/e31x_periphs.py
index 0b166e5bc..330cd830e 100644
--- a/mpm/python/usrp_mpm/periph_manager/e31x_periphs.py
+++ b/mpm/python/usrp_mpm/periph_manager/e31x_periphs.py
@@ -33,23 +33,40 @@ class MboardRegsControl(object):
Control the FPGA Motherboard registers
"""
# Motherboard registers
- MB_COMPAT_NUM = 0x0000
- MB_DATESTAMP = 0x0004
- MB_GIT_HASH = 0x0008
- MB_SCRATCH = 0x000C
- MB_NUM_CE = 0x0010
- MB_NUM_IO_CE = 0x0014
- MB_CLOCK_CTRL = 0x0018
- MB_XADC_RB = 0x001C
- MB_BUS_CLK_RATE = 0x0020
- MB_BUS_COUNTER = 0x0024
- MB_GPIO_MASTER = 0x0030
- MB_GPIO_RADIO_SRC = 0x0034
- MB_GPS_CTRL = 0x0038
- MB_GPS_STATUS = 0x003C
- MB_DBOARD_CTRL = 0x0040
- MB_DBOARD_STATUS = 0x0044
- MB_XBAR_BASEPORT = 0x0048
+ MB_COMPAT_NUM = 0x0000
+ MB_DATESTAMP = 0x0004
+ MB_GIT_HASH = 0x0008
+ MB_SCRATCH = 0x000C
+ MB_DEVICE_ID = 0x0010
+ MB_RFNOC_INFO = 0x0014
+ MB_CLOCK_CTRL = 0x0018
+ MB_XADC_RB = 0x001C
+ MB_BUS_CLK_RATE = 0x0020
+ MB_BUS_COUNTER = 0x0024
+ MB_SFP_PORT_INFO = 0x0028
+ MB_GPIO_CTRL = 0x002C
+ MB_GPIO_MASTER = 0x0030
+ MB_GPIO_RADIO_SRC = 0x0034
+ MB_GPS_CTRL = 0x0038
+ MB_GPS_STATUS = 0x003C
+ MB_DBOARD_CTRL = 0x0040
+ MB_DBOARD_STATUS = 0x0044
+ MB_NUM_TIMEKEEPERS = 0x0048
+ # Timekeeper registers
+ MB_TIME_NOW_LO = 0x1000
+ MB_TIME_NOW_HI = 0x1004
+ MB_TIME_EVENT_LO = 0x1008
+ MB_TIME_EVENT_HI = 0x100C
+ MB_TIME_CTRL = 0x1010
+ MB_TIME_LAST_PPS_LO = 0x1014
+ MB_TIME_LAST_PPS_HI = 0x1018
+ MB_TIME_BASE_PERIOD_LO = 0x101C
+ MB_TIME_BASE_PERIOD_HI = 0x1020
+ MB_TIMEKEEPER_OFFSET = 12
+
+ # Bitfield locations for the MB_RFNOC_INFO register.
+ MB_RFNOC_INFO_PROTO_VER = 0
+ MB_RFNOC_INFO_CHDR_WIDTH = 16
# Bitfield locations for the MB_CLOCK_CTRL register.
MB_CLOCK_CTRL_PPS_SEL_INT = 0
@@ -100,6 +117,26 @@ class MboardRegsControl(object):
major = (compat_number>>16) & 0xff
return (major, minor)
+ def get_proto_ver(self):
+ """
+ Return RFNoC protocol version
+ """
+ with self.regs:
+ reg_val = self.peek32(self.MB_RFNOC_INFO)
+ proto_ver = (reg_val & 0x0000ffff) >> self.MB_RFNOC_INFO_PROTO_VER
+ self.log.trace("Read RFNOC_PROTO_VER 0x{:08X}".format(proto_ver))
+ return proto_ver
+
+ def get_chdr_width(self):
+ """
+ Return RFNoC CHDR width
+ """
+ with self.regs:
+ reg_val = self.peek32(self.MB_RFNOC_INFO)
+ chdr_width = (reg_val & 0xffff0000) >> self.MB_RFNOC_INFO_CHDR_WIDTH
+ self.log.trace("Read RFNOC_CHDR_WIDTH 0x{:08X}".format(chdr_width))
+ return chdr_width
+
def set_fp_gpio_master(self, value):
"""set driver for front panel GPIO
Arguments:
@@ -136,6 +173,24 @@ class MboardRegsControl(object):
with self.regs:
return self.peek32(self.MB_GPIO_RADIO_SRC) & 0xffffff
+ def set_device_id(self, device_id):
+ """
+ Set device ID
+ """
+ with self.regs:
+ self.log.trace("Writing MB_DEVICE_ID with 0x{:08X}".format(device_id))
+ return self.poke32(self.MB_DEVICE_ID, device_id)
+
+ def get_device_id(self):
+ """
+ Get device ID
+ """
+ with self.regs:
+ regs_val = self.peek32(self.MB_DEVICE_ID)
+ device_id = regs_val & 0x0000ffff
+ self.log.trace("Read MB_DEVICE_ID 0x{:08X}".format(device_id))
+ return device_id
+
def get_build_timestamp(self):
"""
Returns the build date/time for the FPGA image.
@@ -206,6 +261,71 @@ class MboardRegsControl(object):
else:
assert False, "Cannot set to invalid clock source: {}".format(clock_source)
+ def get_num_timekeepers(self):
+ """
+ Return the number of timekeepers
+ """
+ with self.regs:
+ return self.peek32(self.MB_NUM_TIMEKEEPERS)
+
+ def get_timekeeper_time(self, tk_idx, last_pps):
+ """
+ Get the time in ticks
+
+ Arguments:
+ tk_idx: Index of timekeeper
+ next_pps: If True, get time at last PPS. Otherwise, get time now.
+ """
+ addr_lo = \
+ (self.MB_TIME_LAST_PPS_LO if last_pps else self.MB_TIME_NOW_LO) + \
+ tk_idx * self.MB_TIMEKEEPER_OFFSET
+ addr_hi = addr_lo + 4
+ with self.regs:
+ time_lo = self.peek32(addr_lo)
+ time_hi = self.peek32(addr_hi)
+ return time_hi << 32 | time_lo
+
+
+ def set_timekeeper_time(self, tk_idx, ticks, next_pps):
+ """
+ Set the time in ticks
+
+ Arguments:
+ tk_idx: Index of timekeeper
+ ticks: Time in ticks
+ next_pps: If True, set time at next PPS. Otherwise, set time now.
+ """
+ addr_lo = \
+ self.MB_TIME_EVENT_LO + tk_idx * self.MB_TIMEKEEPER_OFFSET
+ addr_hi = addr_lo + 4
+ addr_ctrl = \
+ self.MB_TIME_CTRL + tk_idx * self.MB_TIMEKEEPER_OFFSET
+ time_lo = ticks & 0xFFFFFFFF
+ time_hi = (ticks > 32) & 0xFFFFFFFF
+ time_ctrl = 0x2 if next_pps else 0x1
+ self.log.trace("Setting time on timekeeper %d to %d %s", tk_idx, ticks,
+ ("on next pps" if next_pps else "now"))
+ with self.regs:
+ self.poke32(addr_lo, time_lo)
+ self.poke32(addr_hi, time_hi)
+ self.poke32(addr_ctrl, time_ctrl)
+
+ def set_tick_period(self, tk_idx, period_ns):
+ """
+ Set the time per tick in nanoseconds (tick period)
+
+ Arguments:
+ tk_idx: Index of timekeeper
+ period_ns: Period in nanoseconds
+ """
+ addr_lo = self.MB_TIME_BASE_PERIOD_LO + tk_idx * self.MB_TIMEKEEPER_OFFSET
+ addr_hi = addr_lo + 4
+ period_lo = period_ns & 0xFFFFFFFF
+ period_hi = (period_ns > 32) & 0xFFFFFFFF
+ with self.regs:
+ self.poke32(addr_lo, period_lo)
+ self.poke32(addr_hi, period_hi)
+
def get_fpga_type(self):
"""
Reads the type of the FPGA image currently loaded
@@ -248,7 +368,8 @@ class MboardRegsControl(object):
reg_val = self.peek32(self.MB_DBOARD_CTRL)
if channel_mode == "MIMO":
reg_val = (0b1 << self.MB_DBOARD_CTRL_MIMO)
- self.log.trace("Setting channel mode in AD9361 interface: {}".format("2R2T" if channel_mode == 2 else "1R1T"))
+ self.log.trace("Setting channel mode in AD9361 interface: %s",
+ "2R2T" if channel_mode == 2 else "1R1T")
else:
# Warn if user tries to set either tx0/tx1 in mimo mode
# as both will be set automatically
@@ -269,7 +390,7 @@ class MboardRegsControl(object):
"""
mask = 0b1 << self.MB_DBOARD_STATUS_TX_LOCK
with self.regs:
- reg_val = self.peek32(self.MB_DBOARD_STATUS)
+ reg_val = self.peek32(self.MB_DBOARD_STATUS)
locked = (reg_val & mask) > 0
if not locked:
self.log.warning("TX RF PLL reporting unlocked. ")
@@ -283,15 +404,10 @@ class MboardRegsControl(object):
"""
mask = 0b1 << self.MB_DBOARD_STATUS_RX_LOCK
with self.regs:
- reg_val = self.peek32(self.MB_DBOARD_STATUS)
+ reg_val = self.peek32(self.MB_DBOARD_STATUS)
locked = (reg_val & mask) > 0
if not locked:
self.log.warning("RX RF PLL reporting unlocked. ")
else:
self.log.trace("RX RF PLL locked")
return locked
-
- def get_xbar_baseport(self):
- "Get the RFNoC crossbar base port"
- with self.regs:
- return self.peek32(self.MB_XBAR_BASEPORT)
diff --git a/mpm/python/usrp_mpm/periph_manager/e320.py b/mpm/python/usrp_mpm/periph_manager/e320.py
index f76e6ac3e..04999e505 100644
--- a/mpm/python/usrp_mpm/periph_manager/e320.py
+++ b/mpm/python/usrp_mpm/periph_manager/e320.py
@@ -32,7 +32,7 @@ E320_DEFAULT_CLOCK_SOURCE = 'internal'
E320_DEFAULT_TIME_SOURCE = 'internal'
E320_DEFAULT_ENABLE_GPS = True
E320_DEFAULT_ENABLE_FPGPIO = True
-E320_FPGA_COMPAT = (3, 1)
+E320_FPGA_COMPAT = (4, 0)
E320_MONITOR_THREAD_INTERVAL = 1.0 # seconds
E320_DBOARD_SLOT_IDX = 0
@@ -42,21 +42,15 @@ E320_DBOARD_SLOT_IDX = 0
###############################################################################
class E320XportMgrUDP(XportMgrUDP):
"E320-specific UDP configuration"
- xbar_dev = "/dev/crossbar0"
iface_config = {
'sfp0': {
'label': 'misc-enet-regs',
- 'xbar': 0,
- 'xbar_port': 0,
- 'ctrl_src_addr': 0,
}
}
class E320XportMgrLiberio(XportMgrLiberio):
" E320-specific Liberio configuration "
max_chan = 6
- xbar_dev = "/dev/crossbar0"
- xbar_port = 1
###############################################################################
# Main Class
@@ -139,7 +133,6 @@ class e320(ZynqComponents, PeriphManagerBase):
self._ext_clock_freq = E320_DEFAULT_EXT_CLOCK_FREQ
self._clock_source = None
self._time_source = None
- self._available_endpoints = list(range(256))
self._gpsd = None
self.dboard = self.dboards[E320_DBOARD_SLOT_IDX]
from functools import partial
@@ -266,7 +259,6 @@ class e320(ZynqComponents, PeriphManagerBase):
self.mboard_regs_control.get_build_timestamp()
self._check_fpga_compat()
self._update_fpga_type()
- self.crossbar_base_port = self.mboard_regs_control.get_xbar_baseport()
# Init peripherals
self.enable_gps(
enable=str2bool(
@@ -274,10 +266,7 @@ class e320(ZynqComponents, PeriphManagerBase):
)
)
self.enable_fp_gpio(
- enable=args.get(
- 'enable_fp_gpio',
- E320_DEFAULT_ENABLE_FPGPIO
- )
+ enable=args.get('enable_fp_gpio', E320_DEFAULT_ENABLE_FPGPIO)
)
# Init clocking
self._init_ref_clock_and_time(args)
@@ -285,8 +274,8 @@ class e320(ZynqComponents, PeriphManagerBase):
self._init_gps_sensors()
# Init CHDR transports
self._xport_mgrs = {
- 'udp': E320XportMgrUDP(self.log.getChild('UDP'), args),
- 'liberio': E320XportMgrLiberio(self.log.getChild('liberio')),
+ 'udp': E320XportMgrUDP(self.log, args),
+ 'liberio': E320XportMgrLiberio(self.log),
}
# Spawn status monitoring thread
self.log.trace("Spawning status monitor thread...")
@@ -347,8 +336,6 @@ class e320(ZynqComponents, PeriphManagerBase):
super(e320, self).deinit()
for xport_mgr in itervalues(self._xport_mgrs):
xport_mgr.deinit()
- self.log.trace("Resetting SID pool...")
- self._available_endpoints = list(range(256))
def tear_down(self):
"""
@@ -372,61 +359,37 @@ class e320(ZynqComponents, PeriphManagerBase):
###########################################################################
# Transport API
###########################################################################
- def request_xport(
- self,
- dst_address,
- suggested_src_address,
- xport_type
- ):
- """
- See PeriphManagerBase.request_xport() for docs.
- """
- # Try suggested address first, then just pick the first available one:
- src_address = suggested_src_address
- if src_address not in self._available_endpoints:
- if not self._available_endpoints:
- raise RuntimeError(
- "Depleted pool of SID endpoints for this device!")
- else:
- src_address = self._available_endpoints[0]
- sid = SID(src_address << 16 | dst_address)
- # Note: This SID may change its source address!
- self.log.trace(
- "request_xport(dst=0x%04X, suggested_src_address=0x%04X, xport_type=%s): " \
- "operating on temporary SID: %s",
- dst_address, suggested_src_address, str(xport_type), str(sid))
- # FIXME token!
+ def get_chdr_link_types(self):
+ """
+ This will only ever return a single item (udp or liberio).
+ """
assert self.mboard_info['rpc_connection'] in ('remote', 'local')
if self.mboard_info['rpc_connection'] == 'remote':
- return self._xport_mgrs['udp'].request_xport(
- sid,
- xport_type,
- )
- elif self.mboard_info['rpc_connection'] == 'local':
- return self._xport_mgrs['liberio'].request_xport(
- sid,
- xport_type,
- )
+ return ["udp"]
+ # else:
+ return ["liberio"]
- def commit_xport(self, xport_info):
+ def get_chdr_link_options(self, xport_type):
"""
- See PeriphManagerBase.commit_xport() for docs.
+ Returns a list of dictionaries. Every dictionary contains information
+ about one way to connect to this device in order to initiate CHDR
+ traffic.
+
+ The interpretation of the return value is very highly dependant on the
+ transport type (xport_type).
+ For UDP, the every entry of the list has the following keys:
+ - ipv4 (IP Address)
+ - port (UDP port)
+ - link_rate (bps of the link, e.g. 10e9 for 10GigE)
- Reminder: All connections are incoming, i.e. "send" or "TX" means
- remote device to local device, and "receive" or "RX" means this local
- device to remote device. "Remote device" can be, for example, a UHD
- session.
+ For Liberio, every entry has the following keys:
+ - tx_dev: TX device (/dev/tx-dma*)
+ - rx_dev: RX device (/dev/rx-dma*)
"""
- ## Go, go, go
- assert self.mboard_info['rpc_connection'] in ('remote', 'local')
- sid = SID(xport_info['send_sid'])
- self._available_endpoints.remove(sid.src_ep)
- self.log.debug("Committing transport for SID %s, xport info: %s",
- str(sid), str(xport_info))
- if self.mboard_info['rpc_connection'] == 'remote':
- return self._xport_mgrs['udp'].commit_xport(sid, xport_info)
- elif self.mboard_info['rpc_connection'] == 'local':
- return self._xport_mgrs['liberio'].commit_xport(sid, xport_info)
+ if xport_type not in self._xport_mgrs:
+ self.log.warning("Can't get link options for unknown link type: `{}'.")
+ return []
+ return self._xport_mgrs[xport_type].get_chdr_link_options()
###########################################################################
# Device info
@@ -443,10 +406,43 @@ class e320(ZynqComponents, PeriphManagerBase):
*self.mboard_regs_control.get_compat_number()),
'fpga_version_hash': "{:x}.{}".format(
*self.mboard_regs_control.get_git_hash()),
- 'fpga': self.updateable_components.get('fpga', {}).get('type',""),
+ 'fpga': self.updateable_components.get('fpga', {}).get('type', ""),
})
return device_info
+ def set_device_id(self, device_id):
+ """
+ Sets the device ID for this motherboard.
+ The device ID is used to identify the RFNoC components associated with
+ this motherboard.
+ """
+ self.log.debug("Setting device ID to `{}'".format(device_id))
+ self.mboard_regs_control.set_device_id(device_id)
+
+ def get_device_id(self):
+ """
+ Gets the device ID for this motherboard.
+ The device ID is used to identify the RFNoC components associated with
+ this motherboard.
+ """
+ return self.mboard_regs_control.get_device_id()
+
+ def get_proto_ver(self):
+ """
+ Return RFNoC protocol version
+ """
+ proto_ver = self.mboard_regs_control.get_proto_ver()
+ self.log.debug("RFNoC protocol version supported by this device is {}".format(proto_ver))
+ return proto_ver
+
+ def get_chdr_width(self):
+ """
+ Return RFNoC CHDR width
+ """
+ chdr_width = self.mboard_regs_control.get_chdr_width()
+ self.log.debug("CHDR width supported by the device is {}".format(chdr_width))
+ return chdr_width
+
###########################################################################
# Clock/Time API
###########################################################################
@@ -637,7 +633,7 @@ class e320(ZynqComponents, PeriphManagerBase):
except ValueError:
self.log.warning("Error when converting temperature value")
except KeyError:
- self.log.warning("Can't read temp on thermal_zone".format(sensor))
+ self.log.warning("Can't read temp on thermal_zone {}".format(sensor))
return {
'name': sensor_name,
'type': 'REALNUM',
@@ -753,3 +749,59 @@ class e320(ZynqComponents, PeriphManagerBase):
fpga_type = self.mboard_regs_control.get_fpga_type()
self.log.debug("Updating mboard FPGA type info to {}".format(fpga_type))
self.updateable_components['fpga']['type'] = fpga_type
+
+ #######################################################################
+ # Timekeeper API
+ #######################################################################
+ def get_num_timekeepers(self):
+ """
+ Return the number of timekeepers
+ """
+ return self.mboard_regs_control.get_num_timekeepers()
+
+ def get_timekeeper_time(self, tk_idx, last_pps):
+ """
+ Get the time in ticks
+
+ Arguments:
+ tk_idx: Index of timekeeper
+ next_pps: If True, get time at last PPS. Otherwise, get time now.
+ """
+ return self.mboard_regs_control.get_timekeeper_time(tk_idx, last_pps)
+
+ def set_timekeeper_time(self, tk_idx, ticks, next_pps):
+ """
+ Set the time in ticks
+
+ Arguments:
+ tk_idx: Index of timekeeper
+ ticks: Time in ticks
+ next_pps: If True, set time at next PPS. Otherwise, set time now.
+ """
+ self.mboard_regs_control.set_timekeeper_time(tk_idx, ticks, next_pps)
+
+ def set_tick_period(self, tk_idx, period_ns):
+ """
+ Set the time per tick in nanoseconds (tick period)
+
+ Arguments:
+ tk_idx: Index of timekeeper
+ period_ns: Period in nanoseconds
+ """
+ self.mboard_regs_control.set_tick_period(tk_idx, period_ns)
+
+ def get_clocks(self):
+ """
+ Gets the RFNoC-related clocks present in the FPGA design
+ """
+ return [
+ {
+ 'name': 'radio_clk',
+ 'freq': str(self.dboard.get_master_clock_rate()),
+ 'mutable': 'true'
+ },
+ {
+ 'name': 'bus_clk',
+ 'freq': str(200e6),
+ }
+ ]
diff --git a/mpm/python/usrp_mpm/periph_manager/e320_periphs.py b/mpm/python/usrp_mpm/periph_manager/e320_periphs.py
index 0cf7f59c6..cad5c39ad 100644
--- a/mpm/python/usrp_mpm/periph_manager/e320_periphs.py
+++ b/mpm/python/usrp_mpm/periph_manager/e320_periphs.py
@@ -48,25 +48,40 @@ class MboardRegsControl(object):
Control the FPGA Motherboard registers
"""
# Motherboard registers
- MB_COMPAT_NUM = 0x0000
- MB_DATESTAMP = 0x0004
- MB_GIT_HASH = 0x0008
- MB_SCRATCH = 0x000C
- MB_NUM_CE = 0x0010
- MB_NUM_IO_CE = 0x0014
- MB_CLOCK_CTRL = 0x0018
- MB_XADC_RB = 0x001C
- MB_BUS_CLK_RATE = 0x0020
- MB_BUS_COUNTER = 0x0024
- MB_SFP_PORT_INFO = 0x0028
- MB_GPIO_CTRL = 0x002C
- MB_GPIO_MASTER = 0x0030
- MB_GPIO_RADIO_SRC = 0x0034
- MB_GPS_CTRL = 0x0038
- MB_GPS_STATUS = 0x003C
- MB_DBOARD_CTRL = 0x0040
- MB_DBOARD_STATUS = 0x0044
- MB_XBAR_BASEPORT = 0x0048
+ MB_COMPAT_NUM = 0x0000
+ MB_DATESTAMP = 0x0004
+ MB_GIT_HASH = 0x0008
+ MB_SCRATCH = 0x000C
+ MB_DEVICE_ID = 0x0010
+ MB_RFNOC_INFO = 0x0014
+ MB_CLOCK_CTRL = 0x0018
+ MB_XADC_RB = 0x001C
+ MB_BUS_CLK_RATE = 0x0020
+ MB_BUS_COUNTER = 0x0024
+ MB_SFP_PORT_INFO = 0x0028
+ MB_GPIO_CTRL = 0x002C
+ MB_GPIO_MASTER = 0x0030
+ MB_GPIO_RADIO_SRC = 0x0034
+ MB_GPS_CTRL = 0x0038
+ MB_GPS_STATUS = 0x003C
+ MB_DBOARD_CTRL = 0x0040
+ MB_DBOARD_STATUS = 0x0044
+ MB_NUM_TIMEKEEPERS = 0x0048
+ # Timekeeper registers
+ MB_TIME_NOW_LO = 0x1000
+ MB_TIME_NOW_HI = 0x1004
+ MB_TIME_EVENT_LO = 0x1008
+ MB_TIME_EVENT_HI = 0x100C
+ MB_TIME_CTRL = 0x1010
+ MB_TIME_LAST_PPS_LO = 0x1014
+ MB_TIME_LAST_PPS_HI = 0x1018
+ MB_TIME_BASE_PERIOD_LO = 0x101C
+ MB_TIME_BASE_PERIOD_HI = 0x1020
+ MB_TIMEKEEPER_OFFSET = 12
+
+ # Bitfield locations for the MB_RFNOC_INFO register.
+ MB_RFNOC_INFO_PROTO_VER = 0
+ MB_RFNOC_INFO_CHDR_WIDTH = 16
# Bitfield locations for the MB_CLOCK_CTRL register.
MB_CLOCK_CTRL_PPS_SEL_INT = 0
@@ -122,6 +137,44 @@ class MboardRegsControl(object):
major = (compat_number>>16) & 0xff
return (major, minor)
+ def set_device_id(self, device_id):
+ """
+ Set device ID
+ """
+ with self.regs:
+ self.log.trace("Writing MB_DEVICE_ID with 0x{:08X}".format(device_id))
+ return self.poke32(self.MB_DEVICE_ID, device_id)
+
+ def get_device_id(self):
+ """
+ Get device ID
+ """
+ with self.regs:
+ reg_val = self.peek32(self.MB_DEVICE_ID)
+ device_id = reg_val & 0x0000ffff
+ self.log.trace("Read MB_DEVICE_ID 0x{:08X}".format(device_id))
+ return device_id
+
+ def get_proto_ver(self):
+ """
+ Return RFNoC protocol version
+ """
+ with self.regs:
+ reg_val = self.peek32(self.MB_RFNOC_INFO)
+ proto_ver = (reg_val & 0x0000ffff) >> self.MB_RFNOC_INFO_PROTO_VER
+ self.log.trace("Read RFNOC_PROTO_VER 0x{:08X}".format(proto_ver))
+ return proto_ver;
+
+ def get_chdr_width(self):
+ """
+ Return RFNoC CHDR width
+ """
+ with self.regs:
+ reg_val = self.peek32(self.MB_RFNOC_INFO)
+ chdr_width = (reg_val & 0xffff0000) >> self.MB_RFNOC_INFO_CHDR_WIDTH
+ self.log.trace("Read RFNOC_CHDR_WIDTH 0x{:08X}".format(chdr_width))
+ return chdr_width
+
def enable_fp_gpio(self, enable):
""" Enable front panel GPIO buffers and power supply
and set voltage 3.3 V
@@ -372,7 +425,8 @@ class MboardRegsControl(object):
reg_val = self.peek32(self.MB_DBOARD_CTRL)
if channel_mode == "MIMO":
reg_val = (0b1 << self.MB_DBOARD_CTRL_MIMO)
- self.log.trace("Setting channel mode in AD9361 interface: {}".format("2R2T" if channel_mode == 2 else "1R1T"))
+ self.log.trace("Setting channel mode in AD9361 interface: %s",
+ "2R2T" if channel_mode == 2 else "1R1T")
else:
# Warn if user tries to set either tx0/tx1 in mimo mode
# as both will be set automatically
@@ -393,7 +447,7 @@ class MboardRegsControl(object):
"""
mask = 0b1 << self.MB_DBOARD_STATUS_TX_LOCK
with self.regs:
- reg_val = self.peek32(self.MB_DBOARD_STATUS)
+ reg_val = self.peek32(self.MB_DBOARD_STATUS)
locked = (reg_val & mask) > 0
if not locked:
self.log.warning("TX RF PLL reporting unlocked. ")
@@ -407,7 +461,7 @@ class MboardRegsControl(object):
"""
mask = 0b1 << self.MB_DBOARD_STATUS_RX_LOCK
with self.regs:
- reg_val = self.peek32(self.MB_DBOARD_STATUS)
+ reg_val = self.peek32(self.MB_DBOARD_STATUS)
locked = (reg_val & mask) > 0
if not locked:
self.log.warning("RX RF PLL reporting unlocked. ")
@@ -415,7 +469,67 @@ class MboardRegsControl(object):
self.log.trace("RX RF PLL locked")
return locked
- def get_xbar_baseport(self):
- "Get the RFNoC crossbar base port"
+ def get_num_timekeepers(self):
+ """
+ Return the number of timekeepers
+ """
+ with self.regs:
+ return self.peek32(self.MB_NUM_TIMEKEEPERS)
+
+ def get_timekeeper_time(self, tk_idx, last_pps):
+ """
+ Get the time in ticks
+
+ Arguments:
+ tk_idx: Index of timekeeper
+ next_pps: If True, get time at last PPS. Otherwise, get time now.
+ """
+ addr_lo = \
+ (self.MB_TIME_LAST_PPS_LO if last_pps else self.MB_TIME_NOW_LO) + \
+ tk_idx * self.MB_TIMEKEEPER_OFFSET
+ addr_hi = addr_lo + 4
+ with self.regs:
+ time_lo = self.peek32(addr_lo)
+ time_hi = self.peek32(addr_hi)
+ return time_hi << 32 | time_lo
+
+
+ def set_timekeeper_time(self, tk_idx, ticks, next_pps):
+ """
+ Set the time in ticks
+
+ Arguments:
+ tk_idx: Index of timekeeper
+ ticks: Time in ticks
+ next_pps: If True, set time at next PPS. Otherwise, set time now.
+ """
+ addr_lo = \
+ self.MB_TIME_EVENT_LO + tk_idx * self.MB_TIMEKEEPER_OFFSET
+ addr_hi = addr_lo + 4
+ addr_ctrl = \
+ self.MB_TIME_CTRL + tk_idx * self.MB_TIMEKEEPER_OFFSET
+ time_lo = ticks & 0xFFFFFFFF
+ time_hi = (ticks > 32) & 0xFFFFFFFF
+ time_ctrl = 0x2 if next_pps else 0x1
+ self.log.trace("Setting time on timekeeper %d to %d %s", tk_idx, ticks,
+ ("on next pps" if next_pps else "now"))
+ with self.regs:
+ self.poke32(addr_lo, time_lo)
+ self.poke32(addr_hi, time_hi)
+ self.poke32(addr_ctrl, time_ctrl)
+
+ def set_tick_period(self, tk_idx, period_ns):
+ """
+ Set the time per tick in nanoseconds (tick period)
+
+ Arguments:
+ tk_idx: Index of timekeeper
+ period_ns: Period in nanoseconds
+ """
+ addr_lo = self.MB_TIME_BASE_PERIOD_LO + tk_idx * self.MB_TIMEKEEPER_OFFSET
+ addr_hi = addr_lo + 4
+ period_lo = period_ns & 0xFFFFFFFF
+ period_hi = (period_ns > 32) & 0xFFFFFFFF
with self.regs:
- return self.peek32(self.MB_XBAR_BASEPORT)
+ self.poke32(addr_lo, period_lo)
+ self.poke32(addr_hi, period_hi)
diff --git a/mpm/python/usrp_mpm/periph_manager/n3xx.py b/mpm/python/usrp_mpm/periph_manager/n3xx.py
index 2695dbfba..8d5f83569 100644
--- a/mpm/python/usrp_mpm/periph_manager/n3xx.py
+++ b/mpm/python/usrp_mpm/periph_manager/n3xx.py
@@ -41,7 +41,7 @@ N3XX_DEFAULT_ENABLE_PPS_EXPORT = True
N32X_DEFAULT_QSFP_RATE_PRESET = 'Ethernet'
N32X_DEFAULT_QSFP_DRIVER_PRESET = 'Optical'
N32X_QSFP_I2C_LABEL = 'qsfp-i2c'
-N3XX_FPGA_COMPAT = (5, 3)
+N3XX_FPGA_COMPAT = (6, 0)
N3XX_MONITOR_THREAD_INTERVAL = 1.0 # seconds
# Import daughterboard PIDs from their respective classes
@@ -52,39 +52,24 @@ RHODIUM_PID = Rhodium.pids[0]
###############################################################################
# Transport managers
###############################################################################
+# pylint: disable=too-few-public-methods
class N3xxXportMgrUDP(XportMgrUDP):
" N3xx-specific UDP configuration "
- xbar_dev = "/dev/crossbar0"
iface_config = {
'bridge0': {
'label': 'misc-enet-regs0',
- 'xbar': 0,
- 'xbar_port': 0,
- 'ctrl_src_addr': 0,
},
'sfp0': {
'label': 'misc-enet-regs0',
- 'xbar': 0,
- 'xbar_port': 0,
- 'ctrl_src_addr': 0,
},
'sfp1': {
'label': 'misc-enet-regs1',
- 'xbar': 0,
- 'xbar_port': 1,
- 'ctrl_src_addr': 1,
},
'eth1': {
'label': 'misc-enet-regs0',
- 'xbar': 0,
- 'xbar_port': 0,
- 'ctrl_src_addr': 0,
},
'eth2': {
'label': 'misc-enet-regs1',
- 'xbar': 0,
- 'xbar_port': 1,
- 'ctrl_src_addr': 1,
},
}
bridges = {'bridge0': ['sfp0', 'sfp1', 'bridge0']}
@@ -92,8 +77,7 @@ class N3xxXportMgrUDP(XportMgrUDP):
class N3xxXportMgrLiberio(XportMgrLiberio):
" N3xx-specific Liberio configuration "
max_chan = 10
- xbar_dev = "/dev/crossbar0"
- xbar_port = 2
+# pylint: enable=too-few-public-methods
###############################################################################
# Main Class
@@ -216,7 +200,6 @@ class n3xx(ZynqComponents, PeriphManagerBase):
self._ext_clock_freq = None
self._clock_source = None
self._time_source = None
- self._available_endpoints = list(range(256))
self._bp_leds = None
self._gpsd = None
self._qsfp_retimer = None
@@ -352,7 +335,6 @@ class n3xx(ZynqComponents, PeriphManagerBase):
self.mboard_regs_control.get_git_hash()
self.mboard_regs_control.get_build_timestamp()
self._check_fpga_compat()
- self.crossbar_base_port = self.mboard_regs_control.get_xbar_baseport()
# Init clocking
self.enable_ref_clock(enable=True)
self._ext_clock_freq = None
@@ -463,8 +445,6 @@ class n3xx(ZynqComponents, PeriphManagerBase):
super(n3xx, self).deinit()
for xport_mgr in itervalues(self._xport_mgrs):
xport_mgr.deinit()
- self.log.trace("Resetting SID pool...")
- self._available_endpoints = list(range(256))
def tear_down(self):
"""
@@ -489,66 +469,37 @@ class n3xx(ZynqComponents, PeriphManagerBase):
###########################################################################
# Transport API
###########################################################################
- def request_xport(
- self,
- dst_address,
- suggested_src_address,
- xport_type
- ):
- """
- See PeriphManagerBase.request_xport() for docs.
- """
- # Try suggested address first, then just pick the first available one:
- src_address = suggested_src_address
- if src_address not in self._available_endpoints:
- if len(self._available_endpoints) == 0:
- raise RuntimeError(
- "Depleted pool of SID endpoints for this device!")
- else:
- src_address = self._available_endpoints[0]
- sid = SID(src_address << 16 | dst_address)
- # Note: This SID may change its source address!
- self.log.trace(
- "request_xport(dst=0x%04X, suggested_src_address=0x%04X, xport_type=%s): " \
- "operating on temporary SID: %s",
- dst_address, suggested_src_address, str(xport_type), str(sid))
- # FIXME token!
- assert self.device_info['rpc_connection'] in ('remote', 'local')
- if self.device_info['rpc_connection'] == 'remote':
- if self.device_info['product'] == 'n320':
- alloc_limit = 1
- else:
- alloc_limit = 2
- return self._xport_mgrs['udp'].request_xport(
- sid,
- xport_type,
- alloc_limit
- )
- elif self.device_info['rpc_connection'] == 'local':
- return self._xport_mgrs['liberio'].request_xport(
- sid,
- xport_type,
- )
+ def get_chdr_link_types(self):
+ """
+ This will only ever return a single item (udp or liberio).
+ """
+ assert self.mboard_info['rpc_connection'] in ('remote', 'local')
+ if self.mboard_info['rpc_connection'] == 'remote':
+ return ["udp"]
+ # else:
+ return ["liberio"]
- def commit_xport(self, xport_info):
+ def get_chdr_link_options(self, xport_type):
"""
- See PeriphManagerBase.commit_xport() for docs.
+ Returns a list of dictionaries. Every dictionary contains information
+ about one way to connect to this device in order to initiate CHDR
+ traffic.
+
+ The interpretation of the return value is very highly dependant on the
+ transport type (xport_type).
+ For UDP, the every entry of the list has the following keys:
+ - ipv4 (IP Address)
+ - port (UDP port)
+ - link_rate (bps of the link, e.g. 10e9 for 10GigE)
- Reminder: All connections are incoming, i.e. "send" or "TX" means
- remote device to local device, and "receive" or "RX" means this local
- device to remote device. "Remote device" can be, for example, a UHD
- session.
+ For Liberio, every entry has the following keys:
+ - tx_dev: TX device (/dev/tx-dma*)
+ - rx_dev: RX device (/dev/rx-dma*)
"""
- ## Go, go, go
- assert self.device_info['rpc_connection'] in ('remote', 'local')
- sid = SID(xport_info['send_sid'])
- self._available_endpoints.remove(sid.src_ep)
- self.log.debug("Committing transport for SID %s, xport info: %s",
- str(sid), str(xport_info))
- if self.device_info['rpc_connection'] == 'remote':
- return self._xport_mgrs['udp'].commit_xport(sid, xport_info)
- elif self.device_info['rpc_connection'] == 'local':
- return self._xport_mgrs['liberio'].commit_xport(sid, xport_info)
+ if xport_type not in self._xport_mgrs:
+ self.log.warning("Can't get link options for unknown link type: `{}'.")
+ return []
+ return self._xport_mgrs[xport_type].get_chdr_link_options()
###########################################################################
# Device info
@@ -569,6 +520,39 @@ class n3xx(ZynqComponents, PeriphManagerBase):
})
return device_info
+ def set_device_id(self, device_id):
+ """
+ Sets the device ID for this motherboard.
+ The device ID is used to identify the RFNoC components associated with
+ this motherboard.
+ """
+ self.log.debug("Setting device ID to `{}'".format(device_id))
+ self.mboard_regs_control.set_device_id(device_id)
+
+ def get_device_id(self):
+ """
+ Gets the device ID for this motherboard.
+ The device ID is used to identify the RFNoC components associated with
+ this motherboard.
+ """
+ return self.mboard_regs_control.get_device_id()
+
+ def get_proto_ver(self):
+ """
+ Return RFNoC protocol version
+ """
+ proto_ver = self.mboard_regs_control.get_proto_ver()
+ self.log.debug("RFNoC protocol version supported by this device is {}".format(proto_ver))
+ return proto_ver
+
+ def get_chdr_width(self):
+ """
+ Return RFNoC CHDR width
+ """
+ chdr_width = self.mboard_regs_control.get_chdr_width()
+ self.log.debug("CHDR width supported by the device is {}".format(chdr_width))
+ return chdr_width
+
###########################################################################
# Clock/Time API
###########################################################################
@@ -1065,3 +1049,60 @@ class n3xx(ZynqComponents, PeriphManagerBase):
if self._bp_leds is not None:
# Turn off LINK
self._bp_leds.set(self._bp_leds.LED_LINK, 0)
+
+ #######################################################################
+ # Timekeeper API
+ #######################################################################
+ def get_num_timekeepers(self):
+ """
+ Return the number of timekeepers
+ """
+ return self.mboard_regs_control.get_num_timekeepers()
+
+ def get_timekeeper_time(self, tk_idx, last_pps):
+ """
+ Get the time in ticks
+
+ Arguments:
+ tk_idx: Index of timekeeper
+ next_pps: If True, get time at last PPS. Otherwise, get time now.
+ """
+ return self.mboard_regs_control.get_timekeeper_time(tk_idx, last_pps)
+
+ def set_timekeeper_time(self, tk_idx, ticks, next_pps):
+ """
+ Set the time in ticks
+
+ Arguments:
+ tk_idx: Index of timekeeper
+ ticks: Time in ticks
+ next_pps: If True, set time at next PPS. Otherwise, set time now.
+ """
+ self.mboard_regs_control.set_timekeeper_time(tk_idx, ticks, next_pps)
+
+ def set_tick_period(self, tk_idx, period_ns):
+ """
+ Set the time per tick in nanoseconds (tick period)
+
+ Arguments:
+ tk_idx: Index of timekeeper
+ period_ns: Period in nanoseconds
+ """
+ self.mboard_regs_control.set_tick_period(tk_idx, period_ns)
+
+ def get_clocks(self):
+ """
+ Gets the RFNoC-related clocks present in the FPGA design
+ """
+ return [
+ {
+ 'name': 'radio_clk',
+ 'freq': str(self.dboards[0].get_master_clock_rate()),
+ 'mutable': 'true'
+ },
+ {
+ 'name': 'bus_clk',
+ 'freq': str(200e6),
+ }
+ ]
+
diff --git a/mpm/python/usrp_mpm/periph_manager/n3xx_periphs.py b/mpm/python/usrp_mpm/periph_manager/n3xx_periphs.py
index cb6c237c2..fe005a716 100644
--- a/mpm/python/usrp_mpm/periph_manager/n3xx_periphs.py
+++ b/mpm/python/usrp_mpm/periph_manager/n3xx_periphs.py
@@ -165,12 +165,12 @@ class MboardRegsControl(object):
Control the FPGA Motherboard registers
"""
# Motherboard registers
- M_COMPAT_NUM = 0x0000
+ MB_COMPAT_NUM = 0x0000
MB_DATESTAMP = 0x0004
MB_GIT_HASH = 0x0008
MB_SCRATCH = 0x000C
- MB_NUM_CE = 0x0010
- MB_NUM_IO_CE = 0x0014
+ MB_DEVICE_ID = 0x0010
+ MB_RFNOC_INFO = 0x0014
MB_CLOCK_CTRL = 0x0018
MB_XADC_RB = 0x001C
MB_BUS_CLK_RATE = 0x0020
@@ -179,7 +179,22 @@ class MboardRegsControl(object):
MB_SFP1_INFO = 0x002C
MB_GPIO_MASTER = 0x0030
MB_GPIO_RADIO_SRC = 0x0034
- MB_XBAR_BASEPORT = 0x0038
+ MB_NUM_TIMEKEEPERS = 0x0048
+ # Timekeeper registers
+ MB_TIME_NOW_LO = 0x1000
+ MB_TIME_NOW_HI = 0x1004
+ MB_TIME_EVENT_LO = 0x1008
+ MB_TIME_EVENT_HI = 0x100C
+ MB_TIME_CTRL = 0x1010
+ MB_TIME_LAST_PPS_LO = 0x1014
+ MB_TIME_LAST_PPS_HI = 0x1018
+ MB_TIME_BASE_PERIOD_LO = 0x101C
+ MB_TIME_BASE_PERIOD_HI = 0x1020
+ MB_TIMEKEEPER_OFFSET = 12
+
+ # Bitfield locations for the MB_RFNOC_INFO register.
+ MB_RFNOC_INFO_PROTO_VER = 0
+ MB_RFNOC_INFO_CHDR_WIDTH = 16
# Bitfield locations for the MB_CLOCK_CTRL register.
MB_CLOCK_CTRL_PPS_SEL_INT_10 = 0 # pps_sel is one-hot encoded!
@@ -210,11 +225,49 @@ class MboardRegsControl(object):
2 numbers: (major compat number, minor compat number )
"""
with self.regs:
- compat_number = self.peek32(self.M_COMPAT_NUM)
+ compat_number = self.peek32(self.MB_COMPAT_NUM)
minor = compat_number & 0xff
major = (compat_number>>16) & 0xff
return (major, minor)
+ def set_device_id(self, device_id):
+ """
+ Set device ID
+ """
+ with self.regs:
+ self.log.trace("Writing MB_DEVICE_ID with 0x{:08X}".format(device_id))
+ return self.poke32(self.MB_DEVICE_ID, device_id)
+
+ def get_device_id(self):
+ """
+ Get device ID
+ """
+ with self.regs:
+ reg_val = self.peek32(self.MB_DEVICE_ID)
+ device_id = reg_val & 0x0000ffff
+ self.log.trace("Read MB_DEVICE_ID 0x{:08X}".format(device_id))
+ return device_id
+
+ def get_proto_ver(self):
+ """
+ Return RFNoC protocol version
+ """
+ with self.regs:
+ reg_val = self.peek32(self.MB_RFNOC_INFO)
+ proto_ver = (reg_val & 0x0000ffff) >> self.MB_RFNOC_INFO_PROTO_VER
+ self.log.trace("Read RFNOC_PROTO_VER 0x{:08X}".format(proto_ver))
+ return proto_ver;
+
+ def get_chdr_width(self):
+ """
+ Return RFNoC CHDR width
+ """
+ with self.regs:
+ reg_val = self.peek32(self.MB_RFNOC_INFO)
+ chdr_width = (reg_val & 0xffff0000) >> self.MB_RFNOC_INFO_CHDR_WIDTH
+ self.log.trace("Read RFNOC_CHDR_WIDTH 0x{:08X}".format(chdr_width))
+ return chdr_width
+
def set_fp_gpio_master(self, value):
"""set driver for front panel GPIO
Arguments:
@@ -416,10 +469,6 @@ class MboardRegsControl(object):
.format(sfp0_type, sfp1_type))
return ""
- def get_xbar_baseport(self):
- "Get the RFNoC crossbar base port"
- with self.regs:
- return self.peek32(self.MB_XBAR_BASEPORT)
class RetimerQSFP(DS125DF410):
# (deemphasis, swing)
diff --git a/mpm/python/usrp_mpm/rpc_server.py b/mpm/python/usrp_mpm/rpc_server.py
index 2745ba59d..17179f04d 100644
--- a/mpm/python/usrp_mpm/rpc_server.py
+++ b/mpm/python/usrp_mpm/rpc_server.py
@@ -32,7 +32,7 @@ from usrp_mpm.sys_utils import net
TIMEOUT_INTERVAL = 5.0 # Seconds before claim expires (default value)
TOKEN_LEN = 16 # Length of the token string
# Compatibility number for MPM
-MPM_COMPAT_NUM = (1, 2)
+MPM_COMPAT_NUM = (2, 0)
def no_claim(func):
" Decorator for functions that require no token check "
diff --git a/mpm/python/usrp_mpm/xports/xportmgr_liberio.py b/mpm/python/usrp_mpm/xports/xportmgr_liberio.py
index c9c5ee0f1..f7c1861af 100644
--- a/mpm/python/usrp_mpm/xports/xportmgr_liberio.py
+++ b/mpm/python/usrp_mpm/xports/xportmgr_liberio.py
@@ -8,8 +8,6 @@ Liberio Transport manager
"""
from builtins import object
-from usrp_mpm.liberiotable import LiberioDispatcherTable
-from usrp_mpm import lib
class XportMgrLiberio(object):
"""
@@ -19,14 +17,9 @@ class XportMgrLiberio(object):
liberio_label = 'liberio'
# Number of available DMA channels
max_chan = 4
- # Crossbar to which the Liberio DMA engine is connected
- xbar_dev = "/dev/crossbar0"
- xbar_port = 2
def __init__(self, log):
- self.log = log
- self._dma_dispatcher = LiberioDispatcherTable(self.liberio_label)
- self._data_chan_ctr = 0
+ self.log = log.getChild('Liberio')
def init(self, args):
"""
@@ -36,7 +29,7 @@ class XportMgrLiberio(object):
def deinit(self):
" Clean up after a session terminates "
- self._data_chan_ctr = 0
+ pass
def get_xport_info(self):
"""
@@ -48,50 +41,22 @@ class XportMgrLiberio(object):
In this case, returns an empty dict.
"""
+ assert hasattr(self, 'log')
return {}
- def request_xport(
- self,
- sid,
- xport_type,
- ):
- """
- Return liberio xport info
+ def get_chdr_link_options(self):
"""
- assert xport_type in ('CTRL', 'ASYNC_MSG', 'TX_DATA', 'RX_DATA')
- if xport_type == 'CTRL':
- chan = 0
- elif xport_type == 'ASYNC_MSG':
- chan = 1
- else:
- if self.max_chan > 4:
- chan = 2 + self._data_chan_ctr
- self._data_chan_ctr += 1
- else:
- if xport_type == 'RX_DATA':
- chan = 2
- else:
- chan = 3
- xport_info = {
- 'type': 'liberio',
- 'send_sid': str(sid),
- 'muxed': str(xport_type in ('CTRL', 'ASYNC_MSG')) if (self.max_chan > 4) else 'True',
- 'dma_chan': str(chan),
- 'tx_dev': "/dev/tx-dma{}".format(chan),
- 'rx_dev': "/dev/rx-dma{}".format(chan),
- }
- self.log.trace("Liberio: Chan: {} TX Device: {} RX Device: {}".format(
- chan, xport_info['tx_dev'], xport_info['rx_dev']))
- self.log.trace("Liberio channel is muxed: %s",
- "Yes" if xport_info['muxed'] else "No")
- return [xport_info]
-
- def commit_xport(self, sid, xport_info):
- " Commit liberio transport "
- chan = int(xport_info['dma_chan'])
- xbar_iface = lib.xbar.xbar(self.xbar_dev)
- xbar_iface.set_route(sid.src_addr, self.xbar_port)
- self._dma_dispatcher.set_route(sid.reversed(), chan)
- self.log.trace("Liberio transport successfully committed!")
- return True
+ Returns a list of dictionaries for returning by
+ PeriphManagerBase.get_chdr_link_options().
+ Note: This requires a claim, which means that init() was called, and
+ deinit() was not yet called.
+ """
+ return [
+ {
+ 'tx_dev': "/dev/tx-dma{}".format(chan),
+ 'rx_dev': "/dev/rx-dma{}".format(chan),
+ 'dma_chan': str(chan),
+ }
+ for chan in range(self.max_chan)
+ ]
diff --git a/mpm/python/usrp_mpm/xports/xportmgr_udp.py b/mpm/python/usrp_mpm/xports/xportmgr_udp.py
index 17762bb76..761db4a08 100644
--- a/mpm/python/usrp_mpm/xports/xportmgr_udp.py
+++ b/mpm/python/usrp_mpm/xports/xportmgr_udp.py
@@ -1,5 +1,6 @@
#
# Copyright 2017 Ettus Research, a National Instruments Company
+# Copyright 2019 Ettus Research, a National Instruments Brand
#
# SPDX-License-Identifier: GPL-3.0-or-later
#
@@ -9,10 +10,8 @@ UDP Transport manager
from builtins import object
from six import iteritems, itervalues
-from usrp_mpm.ethtable import EthDispatcherTable
+from usrp_mpm.ethdispatch import EthDispatcherCtrl
from usrp_mpm.sys_utils import net
-from usrp_mpm.mpmtypes import SID
-from usrp_mpm import lib
DEFAULT_BRIDGE_MODE = False
@@ -25,40 +24,38 @@ class XportMgrUDP(object):
# iface_config = {
# 'eth1': { # Add key for every Ethernet iface connected to the FPGA
# 'label': 'misc-enet-regs0', # UIO label for the Eth table
- # 'xbar': 0, # Which crossbar? 0 -> /dev/crossbar0
- # 'xbar_port': 0, # Which port on the crossbar it is connected to
# },
# }
iface_config = {}
bridges = {}
- # The control addresses are typically addresses bound to the controlling
- # UHD session. When the requested source address is below or equal to this
- # number, we override requested SID source addresses based on other logic.
- max_ctrl_addr = 1
def __init__(self, log, args):
- assert len(self.iface_config)
+ assert self.iface_config
assert all((
- all((key in x for key in ('label', 'xbar', 'xbar_port')))
+ all((key in x for key in ('label',)))
for x in itervalues(self.iface_config)
))
- self.log = log
+ self.log = log.getChild('UDP')
self.log.trace("Initializing UDP xport manager...")
self._possible_chdr_ifaces = self.iface_config.keys()
self.log.trace("Identifying available network interfaces...")
- self.chdr_port = EthDispatcherTable.DEFAULT_VITA_PORT[0]
- self._chdr_ifaces = \
- self._init_interfaces(self._possible_chdr_ifaces)
+ self.chdr_port = EthDispatcherCtrl.DEFAULT_VITA_PORT[0]
+ self._chdr_ifaces = self._init_interfaces(self._possible_chdr_ifaces)
self._bridge_mode = args.get('bridge_mode', DEFAULT_BRIDGE_MODE)
self._eth_dispatchers = {}
- self._allocations = {}
- self._previous_block_ep = {}
def _init_interfaces(self, possible_ifaces):
"""
Enumerate all network interfaces that are currently able to stream CHDR
Returns a dictionary iface name -> iface info, where iface info is the
return value of get_iface_info().
+
+ Arguments:
+ - possible_ifaces: A list of strings containing iface names, e.g.
+ ["sfp0", "sfp1"]
+
+ Return Value:
+ A list of dictionaries. The keys are determined by net.get_iface_info().
"""
self.log.trace("Testing available interfaces out of `{}'".format(
list(possible_ifaces)
@@ -83,7 +80,7 @@ class XportMgrUDP(object):
"{} to {}."
.format(len(valid_iface_infos), len(valid_iface_infos_filtered))
)
- if len(valid_iface_infos_filtered):
+ if valid_iface_infos_filtered:
self.log.debug(
"Found CHDR interfaces: `{}'"
.format(", ".join(list(valid_iface_infos.keys())))
@@ -104,10 +101,11 @@ class XportMgrUDP(object):
if len(self._chdr_ifaces) != 1 or bridge_iface != list(self.bridges.keys())[0]:
self.log.error("No Bridge Interfaces found")
raise RuntimeError("No Bridge Interfaces found")
- self.log.info("Updated dispatchers in bridge mode with bridge interface {}".format(
- bridge_iface))
+ self.log.info(
+ "Updated dispatchers in bridge mode with bridge interface {}"
+ .format(bridge_iface))
self._eth_dispatchers = {
- x: EthDispatcherTable(self.iface_config[x]['label'])
+ x: EthDispatcherCtrl(self.iface_config[x]['label'])
for x in self.bridges[bridge_iface]
}
for dispatcher, table in iteritems(self._eth_dispatchers):
@@ -130,7 +128,7 @@ class XportMgrUDP(object):
for iface in self._chdr_ifaces:
if iface not in self._eth_dispatchers:
self._eth_dispatchers[iface] = \
- EthDispatcherTable(self.iface_config[iface]['label'])
+ EthDispatcherCtrl(self.iface_config[iface]['label'])
self._eth_dispatchers[iface].set_ipv4_addr(
self._chdr_ifaces[iface]['ip_addr']
)
@@ -139,8 +137,7 @@ class XportMgrUDP(object):
"""
Call this when the user calls 'init' on the periph manager
"""
- self._chdr_ifaces = \
- self._init_interfaces(self._possible_chdr_ifaces)
+ self._chdr_ifaces = self._init_interfaces(self._possible_chdr_ifaces)
if "bridge_mode" in args:
self._bridge_mode = args.get("bridge_mode")
self._update_dispatchers()
@@ -153,16 +150,10 @@ class XportMgrUDP(object):
args.get('forward_eth', False),
args.get('forward_bcast', False)
)
- if 'preload_ethtables' in args:
- self._preload_ethtables(
- self._eth_dispatchers,
- args['preload_ethtables']
- )
-
def deinit(self):
" Clean up after a session terminates "
- self._allocations = {}
+ pass
def get_xport_info(self):
"""
@@ -174,174 +165,25 @@ class XportMgrUDP(object):
In this case, returns the available IP addresses.
"""
- available_interfaces = \
- self._init_interfaces(self._possible_chdr_ifaces)
+ available_interfaces = self._init_interfaces(self._possible_chdr_ifaces)
return dict(zip(
("addr", "second_addr", "third_addr", "fourth_addr"),
(x['ip_addr'] for x in itervalues(available_interfaces))
))
- def _preload_ethtables(self, eth_dispatchers, table_file):
- """
- Populates the ethernet tables from a JSON file
+ def get_chdr_link_options(self):
"""
- import json
- try:
- eth_table_data = json.load(open(table_file))
- except ValueError as ex:
- self.log.warning(
- "Bad values in preloading table file: %s",
- str(ex)
- )
- return
- self.log.info(
- "Preloading Ethernet dispatch tables from JSON file `%s'.",
- table_file
- )
- for eth_iface, data in iteritems(eth_table_data):
- if eth_iface not in eth_dispatchers:
- self.log.warning(
- "Request to preload eth dispatcher table for "
- "iface `{}', but no such interface is "
- "registered. Known interfaces: {}".format(
- str(eth_iface),
- ",".join(eth_dispatchers.keys())
- )
- )
- continue
- eth_dispatcher = eth_dispatchers[eth_iface]
- self.log.debug("Preloading {} dispatch table".format(eth_iface))
- try:
- for dst_addr, udp_data in iteritems(data):
- sid = SID()
- sid.set_dst_addr(int(dst_addr))
- eth_dispatcher.set_route(
- sid,
- udp_data['ip_addr'],
- udp_data['port'],
- udp_data.get('mac_addr', None)
- )
- except ValueError as ex:
- self.log.warning(
- "Bad values in preloading table file: %s",
- str(ex)
- )
+ Returns a list of dictionaries for returning by
+ PeriphManagerBase.get_chdr_link_options().
- def get_xbar_dev(self, iface):
- """
- Given an Ethernet interface (e.g., 'eth1') returns the crossbar device
- it is connected to.
+ Note: This requires a claim, which means that init() was called, and
+ deinit() was not yet called.
"""
- xbar_idx = self.iface_config[iface]['xbar']
- return "/dev/crossbar{}".format(xbar_idx)
-
- def request_xport(
- self,
- sid,
- xport_type,
- alloc_limit=2
- ):
- """
- Return UDP xport info
- """
- def fixup_sid(sid, iface_name):
- " Modify the source SID (e.g. the UHD SID) "
- if sid.src_addr <= self.max_ctrl_addr:
- sid.src_addr = self.iface_config[iface_name]['ctrl_src_addr']
- return sid
-
- def sort_xport_info(xport):
- """
- We sort xport_info (which is a list of xport) as follows:
- 1. Look at current allocation of xport src_addr (which is the addr
- of host). If the allocation too large, in this case larger or
- equal to 2 (since 2*125 = 250MS/s = max bandwidth of SFP+ port),
- we will use the allocation for sorting.
- 2. Else, we need to look at the destination block. The priority will
- yield to the xport that has the previous destination block
- that is the same as this coming destination block.
- Note: smaller number return is the higher chance to be picked
- """
- sid = SID(xport['send_sid'])
- src_addr = sid.src_addr
- prev_block = -1
- if src_addr in self._previous_block_ep:
- prev_block = self._previous_block_ep[src_addr]
- allocation = int(xport['allocation'])
- if allocation >= alloc_limit:
- return allocation
- else:
- return allocation if prev_block != sid.get_dst_block() else -1;
-
- assert xport_type in ('CTRL', 'ASYNC_MSG', 'TX_DATA', 'RX_DATA')
- allocation_getter = lambda iface: {
- 'CTRL': 0,
- 'ASYNC_MSG': 0,
- 'RX_DATA': self._allocations.get(iface, {}).get('rx', 0),
- 'TX_DATA': self._allocations.get(iface, {}).get('tx', 0),
- }[xport_type]
- xport_info = sorted([
+ return [
{
- 'type': 'UDP',
'ipv4': str(iface_info['ip_addr']),
'port': str(self.chdr_port),
- 'send_sid': str(fixup_sid(sid, iface_name)),
- 'allocation': str(allocation_getter(iface_name)),
- 'xport_type': xport_type,
- 'link_speed': str(iface_info['link_speed'])
+ 'link_rate': str(int(iface_info['link_speed'] * 1e9 / 8))
}
for iface_name, iface_info in iteritems(self._chdr_ifaces)
]
- , key=lambda x: sort_xport_info(x)
- , reverse=False)
- return xport_info
-
- def commit_xport(self, sid, xport_info):
- """
- Commit transport
-
- Saves the transport configuration to the device.
- Returns the status of the commit.
- """
- self.log.trace("Sanity checking xport_info %s...", str(xport_info))
- assert xport_info['type'] == 'UDP'
- assert any([xport_info['ipv4'] == x['ip_addr']
- for x in itervalues(self._chdr_ifaces)])
- assert xport_info['port'] == str(self.chdr_port)
- assert len(xport_info.get('src_ipv4')) > 5
- assert int(xport_info.get('src_port')) > 0
- sender_addr = xport_info['src_ipv4']
- sender_port = int(xport_info['src_port'])
- self.log.trace("Incoming connection is coming from %s:%d",
- sender_addr, sender_port)
- mac_addr = net.get_mac_addr(sender_addr)
- if mac_addr is None:
- raise RuntimeError(
- "Could not find MAC address for IP address {}".format(
- sender_addr))
- self.log.trace("Incoming connection is coming from %s",
- mac_addr)
- eth_iface = net.ip_addr_to_iface(xport_info['ipv4'], self._chdr_ifaces)
- xbar_port = self.iface_config[eth_iface]['xbar_port']
- self.log.trace("Using Ethernet interface %s, crossbar port %d",
- eth_iface, xbar_port)
- xbar_iface = lib.xbar.xbar(self.get_xbar_dev(eth_iface))
- xbar_iface.set_route(sid.src_addr, xbar_port)
- self._eth_dispatchers[eth_iface].set_route(
- sid.reversed(), sender_addr, sender_port)
- self.log.trace("UDP transport successfully committed!")
- self._previous_block_ep[sid.src_addr] = sid.get_dst_block()
- if xport_info.get('xport_type') == 'TX_DATA':
- self._allocations[eth_iface] = \
- {'tx': self._allocations.get(eth_iface, {}).get('tx', 0) + 1}
- if xport_info.get('xport_type') == 'RX_DATA':
- self._allocations[eth_iface] = \
- {'rx': self._allocations.get(eth_iface, {}).get('rx', 0) + 1}
- self.log.trace(
- "New link allocations for %s: TX: %d RX: %d",
- eth_iface,
- self._allocations.get(eth_iface, {}).get('tx', 0),
- self._allocations.get(eth_iface, {}).get('rx', 0),
- )
- return True
-