diff options
-rw-r--r-- | mpm/python/usrp_mpm/periph_manager/CMakeLists.txt | 1 | ||||
-rw-r--r-- | mpm/python/usrp_mpm/periph_manager/base.py | 55 | ||||
-rw-r--r-- | mpm/python/usrp_mpm/periph_manager/common.py | 209 | ||||
-rw-r--r-- | mpm/python/usrp_mpm/periph_manager/e31x.py | 70 | ||||
-rw-r--r-- | mpm/python/usrp_mpm/periph_manager/e31x_periphs.py | 196 | ||||
-rw-r--r-- | mpm/python/usrp_mpm/periph_manager/e320.py | 70 | ||||
-rw-r--r-- | mpm/python/usrp_mpm/periph_manager/e320_periphs.py | 189 | ||||
-rw-r--r-- | mpm/python/usrp_mpm/periph_manager/n3xx.py | 74 | ||||
-rw-r--r-- | mpm/python/usrp_mpm/periph_manager/n3xx_periphs.py | 144 |
9 files changed, 272 insertions, 736 deletions
diff --git a/mpm/python/usrp_mpm/periph_manager/CMakeLists.txt b/mpm/python/usrp_mpm/periph_manager/CMakeLists.txt index 987bc184b..4f8520c12 100644 --- a/mpm/python/usrp_mpm/periph_manager/CMakeLists.txt +++ b/mpm/python/usrp_mpm/periph_manager/CMakeLists.txt @@ -11,6 +11,7 @@ set(USRP_MPM_FILES ${USRP_MPM_FILES}) set(USRP_MPM_PERIPHMGR_FILES ${CMAKE_CURRENT_SOURCE_DIR}/__init__.py.in ${CMAKE_CURRENT_SOURCE_DIR}/base.py + ${CMAKE_CURRENT_SOURCE_DIR}/common.py ${CMAKE_CURRENT_SOURCE_DIR}/n3xx.py ${CMAKE_CURRENT_SOURCE_DIR}/n3xx_periphs.py ${CMAKE_CURRENT_SOURCE_DIR}/e320.py diff --git a/mpm/python/usrp_mpm/periph_manager/base.py b/mpm/python/usrp_mpm/periph_manager/base.py index 4ae30778a..5e5d3889e 100644 --- a/mpm/python/usrp_mpm/periph_manager/base.py +++ b/mpm/python/usrp_mpm/periph_manager/base.py @@ -176,6 +176,8 @@ class PeriphManagerBase(object): # Device initialization (at MPM startup) ########################################################################### def __init__(self): + # This gets set in the child class + self.mboard_regs_control = None # Note: args is a dictionary. assert self.pids assert self.mboard_eeprom_magic is not None @@ -513,7 +515,7 @@ class PeriphManagerBase(object): self.log.trace("Teardown called for Peripheral Manager base.") ########################################################################### - # Misc device status controls and indicators + # RFNoC and Device info ########################################################################### def set_device_id(self, device_id): """ @@ -521,7 +523,8 @@ class PeriphManagerBase(object): The device ID is used to identify the RFNoC components associated with this motherboard. """ - raise NotImplementedError("set_device_id() not implemented.") + self.log.debug("Setting device ID to `{}'".format(device_id)) + self.mboard_regs_control.set_device_id(device_id) def get_device_id(self): """ @@ -529,8 +532,28 @@ class PeriphManagerBase(object): The device ID is used to identify the RFNoC components associated with this motherboard. """ - raise NotImplementedError("get_device_id() not implemented.") + 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 + + ########################################################################### + # Misc device status controls and indicators + ########################################################################### def get_init_status(self): """ Returns the status of the device after its initialization (that happens @@ -611,20 +634,6 @@ 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 ########################################################################### @@ -871,7 +880,7 @@ class PeriphManagerBase(object): """ Return the number of timekeepers """ - raise NotImplementedError("get_num_timekeepers() not implemented.") + return self.mboard_regs_control.get_num_timekeepers() def get_timekeeper_time(self, tk_idx, last_pps): """ @@ -881,8 +890,7 @@ class PeriphManagerBase(object): 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)) + return self.mboard_regs_control.get_timekeeper_time(tk_idx, last_pps) def set_timekeeper_time(self, tk_idx, ticks, next_pps): """ @@ -893,9 +901,7 @@ class PeriphManagerBase(object): 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)) + self.mboard_regs_control.set_timekeeper_time(tk_idx, ticks, next_pps) def set_tick_period(self, tk_idx, period_ns): """ @@ -905,8 +911,7 @@ class PeriphManagerBase(object): tk_idx: Index of timekeeper period_ns: Period in nanoseconds """ - raise NotImplementedError( - "set_tick_period({}) not implemented.".format(tk_idx, period_ns)) + self.mboard_regs_control.set_tick_period(tk_idx, period_ns) def get_clocks(self): """ diff --git a/mpm/python/usrp_mpm/periph_manager/common.py b/mpm/python/usrp_mpm/periph_manager/common.py new file mode 100644 index 000000000..e09be835e --- /dev/null +++ b/mpm/python/usrp_mpm/periph_manager/common.py @@ -0,0 +1,209 @@ +# +# Copyright 2019 Ettus Research, a National Instruments Brand +# +# SPDX-License-Identifier: GPL-3.0-or-later +# +""" +Common code for all MPM devices +""" + +import datetime +from usrp_mpm.sys_utils.uio import UIO + +class MboardRegsCommon(object): + """ + Parent class for mboard regs that are common between *all* MPM devices + """ + # pylint: disable=bad-whitespace + # Global Registers + MB_COMPAT_NUM = 0x0000 + MB_DATESTAMP = 0x0004 + MB_GIT_HASH = 0x0008 + MB_SCRATCH = 0x000C + MB_DEVICE_ID = 0x0010 + MB_RFNOC_INFO = 0x0014 + 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 + # pylint: enable=bad-whitespace + + def __init__(self, label, log): + self.log = log.getChild('MBRegs') + self.regs = UIO( + label=label, + read_only=False + ) + self.poke32 = self.regs.poke32 + self.peek32 = self.regs.peek32 + + ########################################################################### + # Device ID + ########################################################################### + 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 + + ########################################################################### + # FPGA Identification + ########################################################################### + def get_compat_number(self): + """get FPGA compat number + + This function reads back FPGA compat number. + The return is a tuple of 2 numbers: + (major compat number, minor compat number) + """ + with self.regs: + compat_number = self.peek32(self.MB_COMPAT_NUM) + minor = compat_number & 0xff + major = (compat_number>>16) & 0xff + return (major, minor) + + def get_build_timestamp(self): + """ + Returns the build date/time for the FPGA image. + The return value is a datetime string in ISO 8601 format + (YYYY-MM-DD HH:MM:SS.mmmmmm) + """ + with self.regs: + datestamp_rb = self.peek32(self.MB_DATESTAMP) + if datestamp_rb > 0: + dt_str = datetime.datetime( + year=((datestamp_rb>>17)&0x3F)+2000, + month=(datestamp_rb>>23)&0x0F, + day=(datestamp_rb>>27)&0x1F, + hour=(datestamp_rb>>12)&0x1F, + minute=(datestamp_rb>>6)&0x3F, + second=((datestamp_rb>>0)&0x3F)) + self.log.trace("FPGA build timestamp: {}".format(str(dt_str))) + return str(dt_str) + # Compatibility with FPGAs without datestamp capability + return '' + + def get_git_hash(self): + """ + Returns the GIT hash for the FPGA build. + The return is a tuple of 2 numbers: + (short git hash, string: dirty/clean) + """ + with self.regs: + git_hash_rb = self.peek32(self.MB_GIT_HASH) + git_hash = git_hash_rb & 0x0FFFFFFF + tree_dirty = ((git_hash_rb & 0xF0000000) > 0) + dirtiness_qualifier = 'dirty' if tree_dirty else 'clean' + self.log.trace("FPGA build GIT Hash: {:07x} ({})".format( + git_hash, dirtiness_qualifier)) + return (git_hash, dirtiness_qualifier) + + 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 + + ########################################################################### + # Timekeeper API + ########################################################################### + 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) diff --git a/mpm/python/usrp_mpm/periph_manager/e31x.py b/mpm/python/usrp_mpm/periph_manager/e31x.py index 573e75d55..8ba8ff034 100644 --- a/mpm/python/usrp_mpm/periph_manager/e31x.py +++ b/mpm/python/usrp_mpm/periph_manager/e31x.py @@ -509,39 +509,6 @@ 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 ########################################################################### @@ -738,43 +705,6 @@ class e31x(ZynqComponents, PeriphManagerBase): ####################################################################### # 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 diff --git a/mpm/python/usrp_mpm/periph_manager/e31x_periphs.py b/mpm/python/usrp_mpm/periph_manager/e31x_periphs.py index 330cd830e..84c60d2ce 100644 --- a/mpm/python/usrp_mpm/periph_manager/e31x_periphs.py +++ b/mpm/python/usrp_mpm/periph_manager/e31x_periphs.py @@ -1,5 +1,6 @@ # # Copyright 2018 Ettus Research, a National Instruments Company +# Copyright 2019 Ettus Research, a National Instruments Brand # # SPDX-License-Identifier: GPL-3.0-or-later # @@ -7,11 +8,10 @@ E310 peripherals """ -import datetime -import math from usrp_mpm.sys_utils.sysfs_gpio import SysFSGPIO, GPIOBank -from usrp_mpm.sys_utils.uio import UIO +from usrp_mpm.periph_manager.common import MboardRegsCommon +# pylint: disable=too-few-public-methods class FrontpanelGPIO(GPIOBank): """ Abstraction layer for the front panel GPIO @@ -27,18 +27,14 @@ class FrontpanelGPIO(GPIOBank): 0xFF, # use_mask ddr ) +# pylint: enable=too-few-public-methods -class MboardRegsControl(object): +class MboardRegsControl(MboardRegsCommon): """ Control the FPGA Motherboard registers """ + # pylint: disable=bad-whitespace # Motherboard registers - 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 @@ -51,26 +47,12 @@ class MboardRegsControl(object): 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 MB_CLOCK_CTRL_PPS_SEL_EXT = 1 + # FIXME: This value is probably wrong + MB_CLOCK_CTRL_PPS_SEL_GPS = 2 MB_CLOCK_CTRL_REF_CLK_LOCKED = 2 # Bitfield locations for the MB_GPS_CTRL register. @@ -94,48 +76,10 @@ class MboardRegsControl(object): # Bitfield locations for the MB_DBOARD_STATUS register. MB_DBOARD_STATUS_RX_LOCK = 6 MB_DBOARD_STATUS_TX_LOCK = 7 + # pylint: enable=bad-whitespace def __init__(self, label, log): - self.log = log - self.regs = UIO( - label=label, - read_only=False - ) - self.poke32 = self.regs.poke32 - self.peek32 = self.regs.peek32 - - def get_compat_number(self): - """get FPGA compat number - - This function reads back FPGA compat number. - The return is a tuple of - 2 numbers: (major compat number, minor compat number ) - """ - with self.regs: - compat_number = self.peek32(self.MB_COMPAT_NUM) - minor = compat_number & 0xff - 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 + MboardRegsCommon.__init__(self, label, log) def set_fp_gpio_master(self, value): """set driver for front panel GPIO @@ -173,61 +117,6 @@ 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. - The return is datetime string with the ISO 8601 format - (YYYY-MM-DD HH:MM:SS.mmmmmm) - """ - with self.regs: - datestamp_rb = self.peek32(self.MB_DATESTAMP) - if datestamp_rb > 0: - dt_str = datetime.datetime( - year=((datestamp_rb>>17)&0x3F)+2000, - month=(datestamp_rb>>23)&0x0F, - day=(datestamp_rb>>27)&0x1F, - hour=(datestamp_rb>>12)&0x1F, - minute=(datestamp_rb>>6)&0x3F, - second=((datestamp_rb>>0)&0x3F)) - self.log.trace("FPGA build timestamp: {}".format(str(dt_str))) - return str(dt_str) - else: - # Compatibility with FPGAs without datestamp capability - return '' - - def get_git_hash(self): - """ - Returns the GIT hash for the FPGA build. - The return is a tuple of - 2 numbers: (short git hash, bool: is the tree dirty?) - """ - with self.regs: - git_hash_rb = self.peek32(self.MB_GIT_HASH) - git_hash = git_hash_rb & 0x0FFFFFFF - tree_dirty = ((git_hash_rb & 0xF0000000) > 0) - dirtiness_qualifier = 'dirty' if tree_dirty else 'clean' - self.log.trace("FPGA build GIT Hash: {:07x} ({})".format( - git_hash, dirtiness_qualifier)) - return (git_hash, dirtiness_qualifier) - def set_time_source(self, time_source): """ Set time source @@ -261,71 +150,6 @@ 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 diff --git a/mpm/python/usrp_mpm/periph_manager/e320.py b/mpm/python/usrp_mpm/periph_manager/e320.py index d48f90cbd..90cc1c049 100644 --- a/mpm/python/usrp_mpm/periph_manager/e320.py +++ b/mpm/python/usrp_mpm/periph_manager/e320.py @@ -410,39 +410,6 @@ class e320(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 ########################################################################### @@ -753,43 +720,6 @@ class e320(ZynqComponents, PeriphManagerBase): ####################################################################### # 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 diff --git a/mpm/python/usrp_mpm/periph_manager/e320_periphs.py b/mpm/python/usrp_mpm/periph_manager/e320_periphs.py index cad5c39ad..8dcd5a9ba 100644 --- a/mpm/python/usrp_mpm/periph_manager/e320_periphs.py +++ b/mpm/python/usrp_mpm/periph_manager/e320_periphs.py @@ -7,10 +7,9 @@ E320 peripherals """ -import datetime import math from usrp_mpm.sys_utils.sysfs_gpio import SysFSGPIO, GPIOBank -from usrp_mpm.sys_utils.uio import UIO +from usrp_mpm.periph_manager.common import MboardRegsCommon # Map register values to SFP transport types E320_SFP_TYPES = { @@ -43,17 +42,12 @@ class FrontpanelGPIO(GPIOBank): ddr ) -class MboardRegsControl(object): +class MboardRegsControl(MboardRegsCommon): """ Control the FPGA Motherboard registers """ + # pylint: disable=bad-whitespace # Motherboard registers - 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 @@ -66,22 +60,7 @@ class MboardRegsControl(object): 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 + # pylint: enable=bad-whitespace # Bitfield locations for the MB_CLOCK_CTRL register. MB_CLOCK_CTRL_PPS_SEL_INT = 0 @@ -116,64 +95,7 @@ class MboardRegsControl(object): MB_DBOARD_STATUS_TX_LOCK = 7 def __init__(self, label, log): - self.log = log - self.regs = UIO( - label=label, - read_only=False - ) - self.poke32 = self.regs.poke32 - self.peek32 = self.regs.peek32 - - def get_compat_number(self): - """get FPGA compat number - - This function reads back FPGA compat number. - The return is a tuple of - 2 numbers: (major compat number, minor compat number ) - """ - with self.regs: - 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 + MboardRegsCommon.__init__(self, label, log) def enable_fp_gpio(self, enable): """ Enable front panel GPIO buffers and power supply @@ -261,43 +183,6 @@ class MboardRegsControl(object): with self.regs: return self.peek32(self.MB_GPIO_RADIO_SRC) & 0xffffff - def get_build_timestamp(self): - """ - Returns the build date/time for the FPGA image. - The return is datetime string with the ISO 8601 format - (YYYY-MM-DD HH:MM:SS.mmmmmm) - """ - with self.regs: - datestamp_rb = self.peek32(self.MB_DATESTAMP) - if datestamp_rb > 0: - dt_str = datetime.datetime( - year=((datestamp_rb>>17)&0x3F)+2000, - month=(datestamp_rb>>23)&0x0F, - day=(datestamp_rb>>27)&0x1F, - hour=(datestamp_rb>>12)&0x1F, - minute=(datestamp_rb>>6)&0x3F, - second=((datestamp_rb>>0)&0x3F)) - self.log.trace("FPGA build timestamp: {}".format(str(dt_str))) - return str(dt_str) - else: - # Compatibility with FPGAs without datestamp capability - return '' - - def get_git_hash(self): - """ - Returns the GIT hash for the FPGA build. - The return is a tuple of - 2 numbers: (short git hash, bool: is the tree dirty?) - """ - with self.regs: - git_hash_rb = self.peek32(self.MB_GIT_HASH) - git_hash = git_hash_rb & 0x0FFFFFFF - tree_dirty = ((git_hash_rb & 0xF0000000) > 0) - dirtiness_qualifier = 'dirty' if tree_dirty else 'clean' - self.log.trace("FPGA build GIT Hash: {:07x} ({})".format( - git_hash, dirtiness_qualifier)) - return (git_hash, dirtiness_qualifier) - def set_time_source(self, time_source, ref_clk_freq): """ Set time source @@ -469,67 +354,3 @@ class MboardRegsControl(object): self.log.trace("RX RF PLL locked") return locked - 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) diff --git a/mpm/python/usrp_mpm/periph_manager/n3xx.py b/mpm/python/usrp_mpm/periph_manager/n3xx.py index 5510b846d..fc9a91e7d 100644 --- a/mpm/python/usrp_mpm/periph_manager/n3xx.py +++ b/mpm/python/usrp_mpm/periph_manager/n3xx.py @@ -43,6 +43,7 @@ N32X_DEFAULT_QSFP_DRIVER_PRESET = 'Optical' N32X_QSFP_I2C_LABEL = 'qsfp-i2c' N3XX_FPGA_COMPAT = (7, 0) N3XX_MONITOR_THREAD_INTERVAL = 1.0 # seconds +N3XX_BUS_CLK = 200e6 # Import daughterboard PIDs from their respective classes MG_PID = Magnesium.pids[0] @@ -525,39 +526,6 @@ 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 ########################################################################### @@ -1051,43 +1019,6 @@ class n3xx(ZynqComponents, PeriphManagerBase): ####################################################################### # 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 @@ -1100,7 +1031,6 @@ class n3xx(ZynqComponents, PeriphManagerBase): }, { 'name': 'bus_clk', - 'freq': str(200e6), + 'freq': str(N3XX_BUS_CLK), } ] - diff --git a/mpm/python/usrp_mpm/periph_manager/n3xx_periphs.py b/mpm/python/usrp_mpm/periph_manager/n3xx_periphs.py index 9b83816c4..ef2372e62 100644 --- a/mpm/python/usrp_mpm/periph_manager/n3xx_periphs.py +++ b/mpm/python/usrp_mpm/periph_manager/n3xx_periphs.py @@ -1,5 +1,6 @@ # # Copyright 2018 Ettus Research, a National Instruments Company +# Copyright 2019 Ettus Research, a National Instruments Brand # # SPDX-License-Identifier: GPL-3.0-or-later # @@ -7,12 +8,11 @@ N3xx peripherals """ -import datetime from usrp_mpm import lib from usrp_mpm.sys_utils.sysfs_gpio import SysFSGPIO, GPIOBank -from usrp_mpm.sys_utils.uio import UIO from usrp_mpm.sys_utils import i2c_dev from usrp_mpm.chips.ds125df410 import DS125DF410 +from usrp_mpm.periph_manager.common import MboardRegsCommon # Map register values to SFP transport types N3XX_SFP_TYPES = { @@ -160,41 +160,20 @@ class BackpanelGPIO(GPIOBank): 0x7, # ddr ) -class MboardRegsControl(object): +class MboardRegsControl(MboardRegsCommon): """ Control the FPGA Motherboard registers """ - # Motherboard registers - 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_SFP0_INFO = 0x0028 - MB_SFP1_INFO = 0x002C - MB_GPIO_MASTER = 0x0030 - MB_GPIO_RADIO_SRC = 0x0034 - 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 + # pylint: disable=bad-whitespace + # Motherboard registers (on top of the ones in MboardRegsCommon) + MB_CLOCK_CTRL = 0x0018 + MB_XADC_RB = 0x001C + MB_BUS_CLK_RATE = 0x0020 + MB_BUS_COUNTER = 0x0024 + MB_SFP0_INFO = 0x0028 + MB_SFP1_INFO = 0x002C + MB_GPIO_MASTER = 0x0030 + MB_GPIO_RADIO_SRC = 0x0034 # Bitfield locations for the MB_CLOCK_CTRL register. MB_CLOCK_CTRL_PPS_SEL_INT_10 = 0 # pps_sel is one-hot encoded! @@ -207,66 +186,10 @@ class MboardRegsControl(object): MB_CLOCK_CTRL_MEAS_CLK_RESET = 12 # set to 1 to reset mmcm, default is 0 MB_CLOCK_CTRL_MEAS_CLK_LOCKED = 13 # locked indication for meas_clk mmcm MB_CLOCK_CTRL_DISABLE_REF_CLK = 16 # to disable the ref_clk, write a '1' + # pylint: enable=bad-whitespace def __init__(self, label, log): - self.log = log - self.regs = UIO( - label=label, - read_only=False - ) - self.poke32 = self.regs.poke32 - self.peek32 = self.regs.peek32 - - def get_compat_number(self): - """get FPGA compat number - - This function reads back FPGA compat number. - The return is a tuple of - 2 numbers: (major compat number, minor compat number ) - """ - with self.regs: - 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 + MboardRegsCommon.__init__(self, label, log) def set_fp_gpio_master(self, value): """set driver for front panel GPIO @@ -308,43 +231,6 @@ class MboardRegsControl(object): with self.regs: return self.peek32(self.MB_GPIO_RADIO_SRC) & 0xffffff - def get_build_timestamp(self): - """ - Returns the build date/time for the FPGA image. - The return is datetime string with the ISO 8601 format - (YYYY-MM-DD HH:MM:SS.mmmmmm) - """ - with self.regs: - datestamp_rb = self.peek32(self.MB_DATESTAMP) - if datestamp_rb > 0: - dt_str = datetime.datetime( - year=((datestamp_rb>>17)&0x3F)+2000, - month=(datestamp_rb>>23)&0x0F, - day=(datestamp_rb>>27)&0x1F, - hour=(datestamp_rb>>12)&0x1F, - minute=(datestamp_rb>>6)&0x3F, - second=((datestamp_rb>>0)&0x3F)) - self.log.trace("FPGA build timestamp: {}".format(str(dt_str))) - return str(dt_str) - else: - # Compatibility with FPGAs without datestamp capability - return '' - - def get_git_hash(self): - """ - Returns the GIT hash for the FPGA build. - The return is a tuple of - 2 numbers: (short git hash, bool: is the tree dirty?) - """ - with self.regs: - git_hash_rb = self.peek32(self.MB_GIT_HASH) - git_hash = git_hash_rb & 0x0FFFFFFF - tree_dirty = ((git_hash_rb & 0xF0000000) > 0) - dirtiness_qualifier = 'dirty' if tree_dirty else 'clean' - self.log.trace("FPGA build GIT Hash: {:07x} ({})".format( - git_hash, dirtiness_qualifier)) - return (git_hash, dirtiness_qualifier) - def set_time_source(self, time_source, ref_clk_freq): """ Set time source |