aboutsummaryrefslogtreecommitdiffstats
path: root/mpm/python/usrp_mpm/periph_manager/common.py
blob: 6ed7f6c53bfecc5d09ab3d4c8454eca0664f4d9d (plain)
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   = 12
    # 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)