uhd/mpm/python/usrp_mpm/periph_manager/common.py
Martin Braun 025ffdce34 mpm: Move common mboard regs code to common location
This assumes an existence of mboard_regs_control in PeriphManagerBase
and implements most TK controls there. All the *_periphs.py files can
now use a common class for registers, including the TK access, but also
git hash, build date, and device ID access.

This also fixes two issues:
- set_timekeeper_time() and set_tick_period() had a bug that would
  incorrectly calculate the upper 32 bits of their respective registers.
- N3xx had a bug that would swap around set time now and next PPS. This
  got auto-fixed because the common code never had this bug.
2019-11-26 12:21:33 -08:00

209 lines
7.1 KiB
Python

#
# 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)