diff options
author | Aaron Rossetto <aaron.rossetto@ni.com> | 2020-06-10 07:45:40 -0500 |
---|---|---|
committer | Aaron Rossetto <aaron.rossetto@ni.com> | 2020-06-18 09:54:22 -0500 |
commit | ade5e6d57ed00641782595cc574297ab1b235f08 (patch) | |
tree | fcb47757ee44f6dd5507a7f2d51eedf7c67a998a | |
parent | 3af8dcaacfa4bf36dcae3bdbf0b353385b7063c6 (diff) | |
download | uhd-ade5e6d57ed00641782595cc574297ab1b235f08.tar.gz uhd-ade5e6d57ed00641782595cc574297ab1b235f08.tar.bz2 uhd-ade5e6d57ed00641782595cc574297ab1b235f08.zip |
utils: Support expressions for num_ports in block defs
This commit allows the RFNoC image builder utility to support block
definition .yml files where the num_ports values are numerical
expressions using values sourced from the parameters section when the
block is used in an RFNoC image. An example of such an expression for
num_ports is the split stream block, where the number of output ports is
defined as the product of the NUM_PORTS and NUM_BRANCHES parameters:
data:
fpga_iface: axis_chdr
clk_domain: rfnoc_chdr
inputs:
in:
num_ports: NUM_PORTS
outputs:
out:
num_ports: NUM_PORTS*NUM_BRANCHES
In an RFNoC image definition .yml file, these parameters can be
specified when a split stream block is instantiated in an image:
split0:
block_desc: 'split_stream.yml'
parameters:
NUM_PORTS: 2
NUM_BRANCHES: 3
Thus, the split0 instance of the split stream block is configured with 2
input ports and 6 output ports (2*3 from NUM_PORTS*NUM_BRANCHES).
When the RFNoC image builder runs and encounters a block instantiation
where that block has a non-integer string in the num_ports key of its
block definition, it performs a textual replacement of the identifiers
in the string with the corresponding values from the parameters section
of the block's instantiation. If no such parameter corresponding to the
identifier exists, the block definition file's parameters section is
consulted for a default value for the parameter. If no such parameter
can be found in either of these locations, the identifier is left
unchanged in place.
After the text substitution step, the expression is evaluated using
Python's expression evaluator. The expression should evaluate to an
integer value, which is then used as the num_ports value. If the
expression does not evaluate to an integer, or fails to evaluate, an
error will be reported.
-rwxr-xr-x | host/python/uhd/imgbuilder/image_builder.py | 38 |
1 files changed, 36 insertions, 2 deletions
diff --git a/host/python/uhd/imgbuilder/image_builder.py b/host/python/uhd/imgbuilder/image_builder.py index 995e26047..fba87361b 100755 --- a/host/python/uhd/imgbuilder/image_builder.py +++ b/host/python/uhd/imgbuilder/image_builder.py @@ -235,8 +235,42 @@ class ImageBuilderConfig: if "num_ports" in port_info: parameter = port_info["num_ports"] num_ports = parameter - if parameter in block["parameters"]: - num_ports = block["parameters"][parameter] + + # If num_ports isn't an integer, it could be an expression + # using values from the parameters section (e.g., + # NUM_PORTS*NUM_BRANCHES for a stream-splitting block). + # If the parameter doesn't resolve to an integer, treat it + # as an expression that needs to be evaluated, hopefully to + # an integer. + if not isinstance(num_ports, int): + # Create a regex to find identifiers. + regex_ident = re.compile(r'[A-Za-z_][A-Za-z0-9_]*') + + # Get a list of all identifiers in the num_ports + # expression and iterate over them all + idents = re.finditer(regex_ident, num_ports) + for ident in idents: + # If the identifier represents a valid parameter + # in the block, replace the identifier text with + # the value of the parameter. If no matching + # parameter is found, just leave the text in + # place. That may result in an exception being + # thrown from eval(), but we'll catch it and + # report an error a bit later on. + if ident[0] in block["parameters"]: + val = str(block["parameters"][ident[0]]) + num_ports = re.sub(ident[0], val, num_ports) + + # Now, with identifiers resolved to parameter values, + # attempt to evaluate the expression. If eval() fails, + # we'll catch the exception, num_ports will remain non- + # integral, and the if statement after the exception + # is caught will inform the user. + try: + num_ports = eval(num_ports) + except: + pass + # Make sure the parameter resolved to a number if not isinstance(num_ports, int): logging.error( |