Source code for hwtLib.amba.axis_comp.strformat_fn
import re
from typing import Generator, Union
from hwt.hdl.types.bits import Bits
from hwt.hdl.types.stream import HStream
from hwt.hdl.types.struct import HStruct
from hwt.interfaces.structIntf import Interface_to_HdlType
from hwt.synthesizer.typePath import TypePath
from hwt.synthesizer.unit import Unit
from hwtLib.amba.axis import AxiStream
from hwtLib.amba.axis_comp.strformat import AxiS_strFormat, AxiS_strFormatItem
[docs]def _parse_format_groups(f_str: str) -> Generator[Union[str, AxiS_strFormatItem], None, None]:
_tokens = re.split("([\{\}])", f_str) + [None, ]
group_start = None
current_group_body = None
tokens = iter(enumerate(_tokens))
for i, t in tokens:
if t is None:
assert group_start is None
break
t_next = _tokens[i + 1]
if group_start is None:
if t == "{":
if t_next == "{":
# escape of {
next(tokens)
yield t
elif t_next == None:
raise ValueError("Group syntax error: missing closing } for { after reading:", tokens[:i + 1])
else:
group_start = i
elif t == '}':
if t_next == '}':
# escape of }
next(tokens)
yield t
else:
raise ValueError("Group syntax error: closing } without { after reading:",
_tokens[:i + 1])
else:
yield t
else:
if t == "{":
raise ValueError("Group syntax error: { in a group after reading:",
_tokens[:i + 1], ", this group starts at", group_start)
elif t == "}":
if not current_group_body:
raise ValueError("Group syntax error: empty group after reading:",
_tokens[:i + 1])
else:
f = current_group_body.split(":")
name_or_index = f[0]
if len(f) == 1:
format_type = None
digits = None
else:
assert len(f) == 2, f
format_type = f[1]
for i, c in enumerate(format_type):
if not c.isdigit():
break
if i > 0:
digits = int(format_type[:i])
else:
digits = None
format_type = format_type[i:]
if name_or_index.isdigit():
name_or_index = int(name_or_index)
yield AxiS_strFormatItem(
TypePath(name_or_index),
format_type,
digits)
current_group_body = None
group_start = None
else:
assert not current_group_body, (
"group body has to be in a single string chunk",
current_group_body, t)
current_group_body = t
[docs]def axiS_strFormat(parent: Unit, name: str, data_width: int, format_str: str,
*args, **kwargs):
"""
Instanciate an :class:`hwtLib.amba.axis_comp.strformat.AxiS_strFormat` using simplified str.format syntax
The syntax is allows for an utf-8 string with a variable format groups and several escape sequences
in addition to normal string escape sequences.
The escape sequences are (same as :func:`str.format`)
.. code-block:: text
+=======+=================+
| char | escape sequence |
+=======+=================+
| { | {{ |
+-------+-----------------+
| } | }} |
+-------+-----------------+
The syntax for format group is as folowing:
.. code-block:: text
{[index/name]:[nuber_of_digits][type]}
* The index or name specifies the name or the index of the input parameter.
* The width specifies how mahy digits should the output have.
* Format types can be found at :class:`hwtLib.amba.axis_comp.strformat.AxiS_strFormatItem`
* If nuber_of_digits starts with 0 the leading zeros will be used instead of default space char (' ')
* The sign char is included in nuber_of_digits ('{0:04X}'.format(-1) == '-001')
* The type is described in :class:`hwtLib.amba.axis_comp.strformat.AxiS_strFormatItem`
"""
f = AxiS_strFormat()
f.DATA_WIDTH = data_width
# construct input t for configuration
arg_prefix = "arg_"
while True:
arg_names = [f"{arg_prefix}{a_i}" for a_i, _ in enumerate(args)]
if not kwargs:
break
else:
colliding = False
for a in arg_names:
if a in kwargs.keys():
colliding = True
if colliding:
arg_prefix = f"{arg_prefix}0_"
else:
break
in_intf_name_tuples = [
*zip(args, arg_names),
*[
(a, a_name)
for a_name, a in sorted(kwargs.items(), key=lambda x: x[0])
]
]
format_items = tuple(_parse_format_groups(format_str))
for fi in format_items:
if isinstance(fi, str):
continue
elif isinstance(fi.member_path[0], int):
# convert arg index to name in input interface
fi.member_path = TypePath(arg_names[fi.member_path[0]])
arg_usage = {}
for fi in format_items:
if isinstance(fi, str):
continue
usage_cnt = arg_usage.get(fi.member_path, 0) + 1
arg_usage[fi.member_path] = usage_cnt
if fi.format_type == 's':
assert usage_cnt == 1, ("string arguments may be used only once as string is consumed")
for i, (a, a_name) in enumerate(in_intf_name_tuples):
assert arg_usage.get(TypePath(a_name), 0) > 0, ("arg ", i, " named ", a_name, " not used during formating")
if in_intf_name_tuples:
struct_members = []
for a, a_name in in_intf_name_tuples:
if isinstance(a, AxiStream):
t = HStream(Bits(8), start_offsets=[0])
else:
t = Interface_to_HdlType().apply(a)
struct_members.append((t, a_name))
f.INPUT_T = HStruct(*struct_members)
else:
f.INPUT_T = None
f.FORMAT = tuple(format_items)
# connect inputs
setattr(parent, name, f)
for a, a_name in in_intf_name_tuples:
a_in = getattr(f.data_in, a_name)
a_in(a)
return f.data_out