aboutsummaryrefslogtreecommitdiffstats
path: root/mpm/python/usrp_mpm/periph_manager
diff options
context:
space:
mode:
Diffstat (limited to 'mpm/python/usrp_mpm/periph_manager')
-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
7 files changed, 804 insertions, 398 deletions
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)