1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214
|
#
# 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:
"""
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 = 0x100
# Timekeeper control words
MB_TIME_SET_NOW = 0x0001
MB_TIME_SET_NEXT_PPS = 0x0002
MB_TIME_SET_NEXT_SYNC = 0x0004
# 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 = \
self.MB_TIME_EVENT_HI + tk_idx * self.MB_TIMEKEEPER_OFFSET
addr_ctrl = \
self.MB_TIME_CTRL + tk_idx * self.MB_TIMEKEEPER_OFFSET
time_lo = ticks & 0xFFFFFFFF
time_hi = (ticks >> 32) & 0xFFFFFFFF
time_ctrl = self.MB_TIME_SET_NEXT_PPS if next_pps else self.MB_TIME_SET_NOW
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)
|