aboutsummaryrefslogtreecommitdiffstats
path: root/host/lib/ic_reg_maps/common.py
diff options
context:
space:
mode:
authorLars Amsel <lars.amsel@ni.com>2022-04-07 18:32:22 +0200
committerAaron Rossetto <aaron.rossetto@ni.com>2022-06-10 13:24:04 -0500
commit9fb6b67f29d479da2c78814e8e3c90b0a192b508 (patch)
treeb78d0409b1a9935e29b6555e72d74f9a981e8f6c /host/lib/ic_reg_maps/common.py
parent91de4116d8f7c01db36095f46f040e6e9bf815dc (diff)
downloaduhd-9fb6b67f29d479da2c78814e8e3c90b0a192b508.tar.gz
uhd-9fb6b67f29d479da2c78814e8e3c90b0a192b508.tar.bz2
uhd-9fb6b67f29d479da2c78814e8e3c90b0a192b508.zip
uhd: change default into option flag in register definition
This adds support for read only registers in generated interfaces. For this the default is extended to an option string. The old format is still supported for backward compability, so if options string is just a number it will be handled as a writable number. The option string is a comma separated list with key=value pairs. The value is optional and treated as None if missing. common.py now allows to pass in **kwargs to the generate method which is used by gen_zbx_cpld_regs.py to pass a filter function for registers used by mpm only. get_all_addr now has an additional (optional, defaults to false) flag to indicate whether read only addresses are to be returned or not. It also supports type generic for the result to align with get_changed_addr function. The ZBX CPLD CTRL map is adapted accordingly to reflect read only registers. The power registers are flagged as MPM scope only (and not used in ZBX CPLD control of UHD). Co-authored-by: Martin Braun <martin.braun@ettus.com>
Diffstat (limited to 'host/lib/ic_reg_maps/common.py')
-rwxr-xr-xhost/lib/ic_reg_maps/common.py50
1 files changed, 43 insertions, 7 deletions
diff --git a/host/lib/ic_reg_maps/common.py b/host/lib/ic_reg_maps/common.py
index a689872ff..2275c68e2 100755
--- a/host/lib/ic_reg_maps/common.py
+++ b/host/lib/ic_reg_maps/common.py
@@ -235,6 +235,18 @@ def to_num(arg):
"""
return int(eval(arg))
+def is_int(arg):
+ """
+ check whether arg is convertable to an integer
+ (including non-decimal representation)
+ """
+ try:
+ to_num(arg)
+ return True
+ except:
+ return False
+
+
class reg:
def __init__(self, reg_des):
self.is_array = False
@@ -247,6 +259,22 @@ class reg:
'Error parsing register description: "{}"\nWhat: {}'
.format(reg_des, e))
+ def _parse_options(self, optionstr):
+ """
+ `optionstr` contains additional register information in a
+ comma-separated list as key=value pairs.
+
+ For backward compability a number <x> is converted to
+ `default=<x>,rw`
+ which retains the previous behaviour.
+ """
+ if is_int(optionstr):
+ # convert number into option list for backward compability
+ optionstr = f"default={optionstr},rw"
+ options = [option.partition("=") for option in optionstr.split(",")]
+ result = { item[0] : item[2] or None for item in options }
+ return result
+
def parse(self, reg_des):
"""
Parse a regmap line.
@@ -268,15 +296,15 @@ class reg:
initialized to 0. duper_reg is an array of length 128, of 32-bit registers.
"""
x = re.match(
- r'^(\w*)(\[([0-9:]*)\])?\s*(\w*)\[(.*)\]\s*(\w*)\s*(.*)$',
+ r'^(\w*)(\[([0-9:]*)\])?\s*(\w*)\[(.*)\]\s*([=,\w]*)\s*(.*)$',
reg_des)
- name, _, addr_range, addr, bit_range, default, enums = x.groups()
+ name, _, addr_range, addr, bit_range, options, enums = x.groups()
#store variables
self._name = name
self._addr = to_num(addr)
if ':' in bit_range: self._addr_spec = sorted(map(int, bit_range.split(':')))
else: self._addr_spec = int(bit_range), int(bit_range)
- self._default = to_num(default)
+ self.options = self._parse_options(options)
#extract enum
self._enums = list()
if enums:
@@ -313,9 +341,10 @@ class reg:
def get_enums(self): return self._enums
def get_name(self): return self._name
def get_default(self):
+ default_val = to_num(self.options.get("default", "0"))
for key, val in self.get_enums():
- if val == self._default: return ('%s_%s'%(self.get_name(), key)).upper()
- return self._default
+ if val == default_val: return ('%s_%s'%(self.get_name(), key)).upper()
+ return default_val
def get_type(self):
if self.get_enums(): return '%s_t'%self.get_name()
return 'uint%d_t'%max(2**math.ceil(math.log(self.get_bit_width(), 2)), 8)
@@ -334,6 +363,11 @@ class reg:
Return the step size for register arrays
"""
return self._addr_step
+ def is_readonly(self):
+ """
+ Check whether register is marked as readonly via option string
+ """
+ return "ro" in self.options
class mreg:
def __init__(self, mreg_des, regs):
@@ -353,7 +387,7 @@ class mreg:
def get_type(self):
return 'uint%d_t'%max(2**math.ceil(math.log(self.get_bit_width(), 2)), 8)
-def generate(name, regs_tmpl, body_tmpl='', py_body_tmpl='', file=__file__, append=False):
+def generate(name, regs_tmpl, body_tmpl='', py_body_tmpl='', file=__file__, append=False, **kwargs):
# determine if the destination file is a Python or C++ header file
out_file = sys.argv[1]
if out_file.endswith('.py'): # Write a Python file
@@ -373,15 +407,17 @@ def generate(name, regs_tmpl, body_tmpl='', py_body_tmpl='', file=__file__, appe
regs.append(reg(entry))
#evaluate the body template with the list of registers
- body = '\n '.join(parse_tmpl(body_template, regs=regs).splitlines())
+ body = '\n '.join(parse_tmpl(body_template, **dict(kwargs, regs=regs)).splitlines())
#evaluate the code template with the parsed registers and arguments
code = parse_tmpl(template,
+ **dict(kwargs,
name=name,
regs=regs,
mregs=mregs,
body=body,
file=file,
+ )
)
#write the generated code to file specified by argv1