Skip to content

Commit 504aa89

Browse files
committed
Clean up match_bitpattern by simplifying some logic, renaming some variables, adding some comments, and improving its documentation.
1 parent 3800ffc commit 504aa89

File tree

2 files changed

+37
-29
lines changed

2 files changed

+37
-29
lines changed

docs/helpers.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ Control Flow Hardware
4040
.. autofunction:: pyrtl.corecircuits.enum_mux
4141
.. autofunction:: pyrtl.corecircuits.bitfield_update
4242
.. autofunction:: pyrtl.corecircuits.bitfield_update_set
43+
.. autoclass:: pyrtl.helperfuncs.MatchedFields
4344
.. autofunction:: pyrtl.helperfuncs.match_bitpattern
4445

4546
Creating Lists of WireVectors

pyrtl/helperfuncs.py

Lines changed: 36 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -191,44 +191,44 @@ def __exit__(self, *execinfo):
191191

192192

193193
def match_bitpattern(w: WireVector, bitpattern: str,
194-
field_map: dict = None) -> MatchedFields:
195-
"""Returns a single-bit WireVector that is 1 if and only if ``w`` matches
194+
field_map: dict[str, str] = None) -> MatchedFields:
195+
"""Returns a single-bit ``WireVector`` that is 1 if and only if ``w`` matches
196196
the ``bitpattern``, and a tuple containing the matched fields, if any.
197197
Compatible with the ``with`` statement.
198198
199-
:param WireVector w: The WireVector to be compared to the bitpattern
199+
:param w: The ``WireVector`` to be compared to the ``bitpattern``
200200
:param bitpattern: A string holding the pattern (of bits and wildcards) to
201201
match
202202
:param field_map: (optional) A map from single-character field name in the
203-
bitpattern to the desired name of field in the returned namedtuple. If
204-
given, all non-"1"/"0"/"?" characters in the `bitpattern` must be
203+
bitpattern to the desired name of field in the returned ``namedtuple``. If
204+
given, all non-``1``/``0``/``?`` characters in the ``bitpattern`` must be
205205
present in the map.
206-
:return: A tuple of 1-bit WireVector carrying the result of the comparison,
207-
followed by a named tuple containing the matched fields, if any.
206+
:return: A tuple of a 1-bit ``WireVector`` carrying the result of the comparison,
207+
followed by a ``namedtuple`` containing the matched fields, if any.
208208
209-
This function will compare a multi-bit WireVector to a specified pattern of
209+
This function will compare a multi-bit ``WireVector`` to a specified pattern of
210210
bits, where some of the pattern can be "wildcard" bits. If any of the
211211
``1`` or ``0`` values specified in the bitpattern fail to match the
212-
WireVector during execution, a ``0`` will be produced, otherwise the value
212+
``WireVector`` during execution, a ``0`` will be produced, otherwise the value
213213
carried on the wire will be ``1``. The wildcard characters can be any
214214
other alphanumeric character, with characters other than ``?`` having
215215
special functionality (see below). The string must have length equal to
216-
the WireVector specified, although whitespace and underscore characters
216+
the ``WireVector`` specified, although whitespace and underscore characters
217217
will be ignored and can be used for pattern readability.
218218
219219
For all other characters besides ``1``, ``0``, or ``?``, a tuple of
220-
WireVectors will be returned as the second return value. Each character
220+
``WireVectors`` will be returned as the second return value. Each character
221221
will be treated as the name of a field, and non-consecutive fields with the
222222
same name will be concatenated together, left-to-right, into a single field
223223
in the resultant tuple. For example, ``01aa1?bbb11a`` will match a string
224224
such as ``010010100111``, and the resultant matched fields are::
225225
226226
(a, b) = (0b001, 0b100)
227227
228-
where the ``a`` field is the concenation of bits 9, 8, and 0, and the ``b``
228+
where the ``a`` field is the concatenation of bits 9, 8, and 0, and the ``b``
229229
field is the concenation of bits 5, 4, and 3. Thus, arbitrary characters
230230
beside ``?`` act as wildcard characters for the purposes of matching, with
231-
the additional benefit of returning the WireVectors corresponding to those
231+
the additional benefit of returning the ``WireVectors`` corresponding to those
232232
fields.
233233
234234
A prime example of this is for decoding instructions. Here we decode some
@@ -253,42 +253,49 @@ def match_bitpattern(w: WireVector, bitpattern: str,
253253
254254
m, _ = match_bitpattern(w, '0101') # basically the same as w == '0b0101'
255255
m, _ = match_bitpattern(w, '01?1') # m will be true when w is '0101' or '0111'
256-
m, _ = match_bitpattern(w, '??01') # m be true when last two bits of w are '01'
256+
m, _ = match_bitpattern(w, '??01') # m will be true when last two bits of w are '01'
257257
m, _ = match_bitpattern(w, '??_0 1') # spaces/underscores are ignored, same as line above
258-
m, (a, b) = match_pattern(w, '01aa1?bbb11a') # all bits with same letter make up same field
259-
m, fs = match_pattern(w, '01aa1?bbb11a', {'a': 'foo', 'b': 'bar'}) # fields fs.foo, fs.bar
258+
# All bits with the same letter make up same field.
259+
m, (a, b) = match_bitpattern(w, '01aa1?bbb11a')
260+
# Fields will be named `fs.foo` and `fs.bar`.
261+
m, fs = match_bitpattern(w, '01aa1?bbb11a', {'a': 'foo', 'b': 'bar'})
260262
261263
"""
262264
w = as_wires(w)
263265
if not isinstance(bitpattern, str):
264266
raise PyrtlError('bitpattern must be a string')
265-
nospace_string = ''.join(bitpattern.replace('_', '').split())
266-
if len(w) != len(nospace_string):
267+
bitpattern = bitpattern.replace("_", "").replace(" ", "")
268+
if len(w) != len(bitpattern):
267269
raise PyrtlError('bitpattern string different length than wirevector provided')
268-
lsb_first_string = nospace_string[::-1] # flip so index 0 is lsb
270+
# Reverse ``bitpattern`` so index 0 is the least significant bit. This makes
271+
# ``w[i]`` and ``reversed_bitpattern[i]`` refer to the same bit ``i``.
272+
reversed_bitpattern = bitpattern[::-1]
269273

270-
zero_bits = [w[index] for index, x in enumerate(lsb_first_string) if x == '0']
271-
one_bits = [w[index] for index, x in enumerate(lsb_first_string) if x == '1']
274+
zero_bits = [w[index] for index, x in enumerate(reversed_bitpattern) if x == '0']
275+
one_bits = [w[index] for index, x in enumerate(reversed_bitpattern) if x == '1']
272276
match = rtl_all(*one_bits) & ~rtl_any(*zero_bits)
273277

274-
# Since only Python 3.7 and above guarantees maintaining insertion order in dictionaries,
275-
# do all of this to make sure we can maintain the ordering in the returned Tuple.
276-
# Order of fields is determined based on left-to-right ordering in original string.
277-
def field_name(name):
278+
def field_name(name: str) -> str:
279+
"""Retrieve a field's name from ``field_map``."""
278280
if field_map is not None:
279281
if name not in field_map:
280282
raise PyrtlError('field_map argument has been given, '
281283
'but %s field is not present' % name)
282284
return field_map[name]
283285
return name
284286

287+
# ``fields`` maps from ``field_name`` to a list of WireVectors that match
288+
# ``field_name``.
285289
fields = collections.defaultdict(list)
286-
for i, c in enumerate(lsb_first_string):
290+
for i, c in enumerate(reversed_bitpattern):
287291
if c not in '01?':
288292
fields[c].append(w[i])
289-
fields = sorted(fields.items(), key=lambda m: nospace_string.index(m[0])) # now list of tuples
290-
Fields = collections.namedtuple('Fields', ' '.join(field_name(name) for name, _ in fields))
291-
fields = Fields(**{field_name(k): concat_list(l) for k, l in fields})
293+
# Sort ``fields`` by each field's position in ``bitpattern`` and convert ``fields``
294+
# to a list of tuples.
295+
fields = sorted(fields.items(), key=lambda m: bitpattern.index(m[0]))
296+
Fields = collections.namedtuple('Fields', [field_name(name) for name, _ in fields])
297+
fields = Fields(**{field_name(name): concat_list(wirevector_list)
298+
for name, wirevector_list in fields})
292299

293300
return MatchedFields(match, fields)
294301

0 commit comments

Comments
 (0)