Skip to content

Commit 137ff35

Browse files
committed
Add readability aliases for Gate.op_param. These aliases assign names (reset_value, memid, etc) and types to op_param values, which improve code readability.
Add documentation, examples, and tests for these aliases. Update `output_to_verilog` to use these aliases. Also improve documentation for `memid` and clean up LogicNet's `op` table.
1 parent 0a57cd4 commit 137ff35

File tree

6 files changed

+254
-83
lines changed

6 files changed

+254
-83
lines changed

pyrtl/core.py

Lines changed: 56 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -52,86 +52,77 @@ class LogicNet(NamedTuple):
5252
into ``out``
5353
``n`` ``None`` ``a1, a2`` ``out`` NAND two wires together, put result
5454
into ``out``
55-
``~`` ``None`` ``a1`` ``out`` invert one wire, put result into
56-
``out``
57-
``+`` ``None`` ``a1, a2`` ``out`` add ``a1`` and ``a2``, put result into
58-
``out``
55+
``~`` ``None`` ``a1`` ``out`` Invert one wire, put result into
56+
``out``.
57+
``+`` ``None`` ``a1, a2`` ``out`` Add ``a1`` and ``a2``, put result into
58+
``out``.
5959
6060
``len(out) == max(len(a1), len(a2)) + 1``
6161
6262
Performs *unsigned* addition. Use
6363
:func:`signed_add` for signed
6464
addition.
65-
``-`` ``None`` ``a1, a2`` ``out`` subtract ``a2`` from ``a1``, put
66-
result into ``out``
65+
``-`` ``None`` ``a1, a2`` ``out`` Subtract ``a2`` from ``a1``, put
66+
result into ``out``.
6767
6868
``len(out) == max(len(a1), len(a2)) + 1``
6969
7070
Performs *unsigned* subtraction. Use
7171
:func:`signed_sub` for signed
7272
subtraction.
73-
``*`` ``None`` ``a1, a2`` ``out`` multiply ``a1`` & ``a2``, put result
74-
into ``out``
73+
``*`` ``None`` ``a1, a2`` ``out`` Multiply ``a1`` and ``a2``, put result
74+
into ``out``.
7575
7676
``len(out) == len(a1) + len(a2)``
7777
7878
Performs *unsigned* multiplication.
7979
Use :func:`signed_mult` for signed
8080
multiplication.
81-
``=`` ``None`` ``a1, a2`` ``out`` check ``a1`` & ``a2`` equal, put
82-
result into ``out`` (0 | 1)
83-
``<`` ``None`` ``a1, a2`` ``out`` check ``a1`` less than ``a2``, put
84-
result into ``out``. ``out`` has
85-
bitwidth 1.
81+
``=`` ``None`` ``a1, a2`` ``out`` Check if ``a1`` and ``a2`` are equal, put
82+
result into ``out``. ``out`` has bitwidth 1.
83+
``<`` ``None`` ``a1, a2`` ``out`` Check if ``a1`` is less than ``a2``, put
84+
result into ``out``. ``out`` has bitwidth 1.
8685
8786
Performs *unsigned* comparison. Use
8887
:func:`signed_lt` for signed less
8988
than.
90-
``>`` ``None`` ``a1, a2`` ``out`` check ``a1`` greater than ``a2``, put
91-
result into ``out`` ``out`` has
92-
bitwidth 1.
89+
``>`` ``None`` ``a1, a2`` ``out`` Check if ``a1`` is greater than ``a2``, put
90+
result into ``out`` ``out`` has bitwidth 1.
9391
9492
Performs *unsigned* comparison. Use
9593
:func:`signed_gt` for signed greater
9694
than.
97-
``w`` ``None`` ``w1`` ``w2`` connects ``w1`` to ``w2``
98-
99-
directional wire with no logical
100-
operation
101-
``x`` ``None`` ``x``, ``out`` multiplexer:
102-
103-
when ``x`` == 0 connect ``a1`` to
104-
``out``
105-
106-
when ``x`` == 1 connect ``a2`` to
107-
``out``
108-
109-
``x`` must be 1-bit and ``len(a1) == len(a2)``
95+
``w`` ``None`` ``w1`` ``w2`` Connects ``w1`` to ``w2``. This is a
96+
directional wire with no logical function.
97+
``x`` ``None`` ``x``, ``out`` Multiplexer:
11098
``a1, a2``
111-
``c`` ``None`` ``*args`` ``out`` concatenates ``*args`` (wires) into
112-
single WireVector
113-
114-
puts first arg at MSB, last arg at LSB
115-
``s`` ``sel`` ``wire`` ``out`` selects bits from wire based on
116-
``sel`` (slicing syntax)
117-
118-
puts selected bits into ``out``
119-
``r`` ``None`` ``next`` ``r1`` on positive clock edge: copies
120-
``next`` to ``r1``
121-
``m`` ``memid``, ``addr`` ``data`` read address addr of mem (with id
122-
``memid``), put it into ``data``
123-
124-
``mem``
125-
``@`` ``memid``, ``addr`` write data to mem (with id ``memid``)
126-
at address ``addr``
127-
128-
request write enable (``wr_en``)
129-
130-
``mem`` ``data``,
131-
132-
``wr_en``
99+
When ``x == 0``, connect ``a1`` to ``out``.
100+
101+
When ``x == 1``, connect ``a2`` to ``out``.
102+
103+
``x`` must be 1-bit and ``len(a1) == len(a2)``.
104+
``c`` ``None`` ``*args`` ``out`` Concatenates :attr:`args` into a single
105+
:class:`.WireVector`.
106+
107+
The first :attr:`arg<args>` becomes the most
108+
significant bits, and the last
109+
:attr:`arg<args>` becomes the least significant
110+
bits.
111+
``s`` ``sel`` ``wire`` ``out`` Selects bits from ``wire`` based on
112+
``sel`` (slicing syntax).
113+
114+
Puts the selected bits into ``out``.
115+
``r`` ``None`` ``next`` ``r1`` On positive clock edge: copy ``next`` to
116+
``r1``.
117+
``m`` ``memid``, ``addr`` ``data`` Read address ``addr`` of :class:`MemBlock`
118+
``mem`` ``mem`` (with :attr:`~MemBlock.id` ``memid``),
119+
put the data read into ``data``.
120+
121+
``@`` ``memid``, ``addr`` Write ``data`` to :class:`MemBlock` ``mem``
122+
``mem`` ``data``, (with :attr:`~MemBlock.id` ``memid``) at
123+
``wr_en`` address ``addr``, if ``wr_en`` is ``1``. This
124+
is the only :attr:`op` with no :attr:`dests`.
133125
===== ========== ========== ======== ====
134-
135126
"""
136127

137128
op: str
@@ -275,10 +266,9 @@ class Block:
275266
Each takes exactly two :class:`~LogicNet.args`, and they should perform the
276267
arithmetic or logical operation specified.
277268
278-
:class:`ops<LogicNet.op>`: ``&``, ``|``, ``^``, ``n``, ``~``, ``+``, ``-``,
279-
``*``.
269+
:class:`ops<LogicNet.op>`: ``&``, ``|``, ``^``, ``n``, ``~``, ``+``, ``-``, ``*``.
280270
281-
All inputs must be the same :attr:`~WireVector.bitwidth`. Logical operations
271+
All inputs must be the same :attr:`~WireVector.bitwidth`. Logical operations
282272
produce as many bits as are in the input, while ``+`` and ``-`` produce ``n + 1``
283273
bits, and ``*`` produces ``2 * n`` bits.
284274
@@ -313,21 +303,21 @@ class Block:
313303
- The ``m`` :class:`~LogicNet.op` is a memory block read port, which supports async
314304
reads (acting like combinational logic). Multiple read (and write) ports are
315305
possible to the same memory but each ``m`` defines only one of those. The
316-
:class:`~LogicNet.op_param` is a tuple containing two references: the ``memid``,
317-
and a reference to the :class:`MemBlock` containing this port. The
318-
:class:`MemBlock` should only be used for debug and sanity checks. Each read port
319-
has one ``addr`` (an :class:`arg<LogicNet.args>`) and one ``data`` (a
320-
:class:`dest<LogicNet.dests>`).
306+
:class:`~LogicNet.op_param` is a tuple containing two values: the
307+
:attr:`MemBlock.id` (``memid``), and a reference to the :class:`MemBlock`
308+
containing this port. The :class:`MemBlock` should only be used for debug and
309+
sanity checks. Each read port has one ``addr`` (an :class:`arg<LogicNet.args>`)
310+
and one ``data`` (a :class:`dest<LogicNet.dests>`).
321311
322312
- The ``@`` (update) :class:`~LogicNet.op` is a memory block write port, which
323313
supports synchronous writes (writes are "latched" at positive edge). Multiple
324314
write (and read) ports are possible to the same memory but each ``@`` defines only
325-
one of those. The :class:`~LogicNet.op_param` is a tuple containing two
326-
references: the ``memid``, and a reference to the :class:`MemBlock`. Writes have
327-
three :class:`~LogicNet.args` (``addr``, ``data``, and write enable ``we_en``).
328-
The :class:`~LogicNet.dests` should be an empty tuple. You will not see a written
329-
value change until the following cycle. If multiple writes happen to the same
330-
address in the same cycle the behavior is currently undefined.
315+
one of those. The :class:`~LogicNet.op_param` is a tuple containing two values:
316+
the :attr:`MemBlock.id` (``memid``), and a reference to the :class:`MemBlock`.
317+
Writes have three :class:`~LogicNet.args` (``addr``, ``data``, and write enable
318+
``we_en``). The :class:`~LogicNet.dests` should be an empty tuple. You will not
319+
see a written value change until the following cycle. If multiple writes happen to
320+
the same address in the same cycle the behavior is currently undefined.
331321
332322
The connecting elements (:class:`~LogicNet.args` and :class:`~LogicNet.dests`)
333323
should be :class:`WireVectors<WireVector>` or derived from :class:`WireVector`, and

pyrtl/gate_graph.py

Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -230,6 +230,11 @@ class Gate:
230230
generally do not appear as actual values on wires. For example, the bits to select
231231
for the ``s`` bit-slice operation are stored as ``op_params``.
232232
233+
.. note::
234+
235+
Consider using the aliases :attr:`const_value`, :attr:`reset_value`,
236+
:attr:`sel`, :attr:`memid`, :attr:`mem` instead, which improve code readability.
237+
233238
.. doctest only::
234239
235240
>>> import pyrtl
@@ -378,6 +383,138 @@ class Gate:
378383
True
379384
"""
380385

386+
@property
387+
def const_value(self) -> int:
388+
"""Readability alias for :attr:`op_param`, returning the value for a
389+
:class:`.Const` (``C``) :attr:`op`.
390+
391+
.. doctest only::
392+
393+
>>> import pyrtl
394+
>>> pyrtl.reset_working_block()
395+
396+
Example::
397+
398+
>>> const = pyrtl.Const(name="const", val=33)
399+
400+
>>> gate_graph = pyrtl.GateGraph()
401+
>>> const_gate = gate_graph.get_gate("const")
402+
>>> const_gate.const_value
403+
33
404+
"""
405+
if self.op != "C":
406+
msg = "const_value is only defined for Const ('C') ops."
407+
raise PyrtlError(msg)
408+
return self.op_param[0]
409+
410+
@property
411+
def reset_value(self) -> int:
412+
"""Readability alias for :attr:`op_param`, returning the reset value for a
413+
:class:`.Register` (``r``) :attr:`op`.
414+
415+
.. doctest only::
416+
417+
>>> import pyrtl
418+
>>> pyrtl.reset_working_block()
419+
420+
Example::
421+
422+
>>> counter = pyrtl.Register(name="counter", bitwidth=8, reset_value=42)
423+
>>> counter.next <<= counter + 1
424+
425+
>>> gate_graph = pyrtl.GateGraph()
426+
>>> counter_gate = gate_graph.get_gate("counter")
427+
>>> counter_gate.reset_value
428+
42
429+
"""
430+
if self.op != "r":
431+
msg = "reset_value is only defined for Register ('r') ops."
432+
raise PyrtlError(msg)
433+
return self.op_param[0]
434+
435+
@property
436+
def sel(self) -> tuple[int]:
437+
"""Readability alias for :attr:`op_param`, returning the bits selected by a
438+
bit-slice (``s``) :attr:`op`.
439+
440+
.. doctest only::
441+
442+
>>> import pyrtl
443+
>>> pyrtl.reset_working_block()
444+
445+
Example::
446+
447+
>>> a = pyrtl.Input(name="a", bitwidth=8)
448+
>>> bit_slice = a[2:6]
449+
>>> bit_slice.name = "bit_slice"
450+
451+
>>> gate_graph = pyrtl.GateGraph()
452+
>>> bit_slice_gate = gate_graph.get_gate("bit_slice")
453+
>>> bit_slice_gate.sel
454+
(2, 3, 4, 5)
455+
"""
456+
if self.op != "s":
457+
msg = "sel is only defined for bit-select ('s') ops."
458+
raise PyrtlError(msg)
459+
return self.op_param
460+
461+
@property
462+
def memid(self) -> int:
463+
"""Readability alias for :attr:`op_param`, returning the :attr:`MemBlock.id`
464+
for a read (``m``) or write (``@``) :attr:`op`.
465+
466+
.. doctest only::
467+
468+
>>> import pyrtl
469+
>>> pyrtl.reset_working_block()
470+
>>> pyrtl.memory._memIndex.internal_index = 0
471+
472+
Example::
473+
474+
>>> addr = pyrtl.Input(name="addr", bitwidth=4)
475+
>>> mem = pyrtl.MemBlock(addrwidth=4, bitwidth=8)
476+
>>> read = mem[addr]
477+
>>> read.name = "read"
478+
479+
>>> gate_graph = pyrtl.GateGraph()
480+
>>> read_gate = gate_graph.get_gate("read")
481+
>>> read_gate.memid
482+
0
483+
>>> mem.id
484+
0
485+
"""
486+
if self.op not in "m@":
487+
msg = "memid is only defined for read ('m') and write ('@') ops."
488+
raise PyrtlError(msg)
489+
return self.op_param[0]
490+
491+
@property
492+
def mem(self) -> int:
493+
"""Readability alias for :attr:`op_param`, returning the :class:`.MemBlock` for
494+
a read (``m``) or write (``@``) :attr:`op`.
495+
496+
.. doctest only::
497+
498+
>>> import pyrtl
499+
>>> pyrtl.reset_working_block()
500+
501+
Example::
502+
503+
>>> addr = pyrtl.Input(name="addr", bitwidth=4)
504+
>>> mem = pyrtl.MemBlock(addrwidth=4, bitwidth=8)
505+
>>> read = mem[addr]
506+
>>> read.name = "read"
507+
508+
>>> gate_graph = pyrtl.GateGraph()
509+
>>> read_gate = gate_graph.get_gate("read")
510+
>>> read_gate.mem is mem
511+
True
512+
"""
513+
if self.op not in "m@":
514+
msg = "mem is only defined for read ('m') and write ('@') ops."
515+
raise PyrtlError(msg)
516+
return self.op_param[1]
517+
381518
def __init__(
382519
self,
383520
logic_net: LogicNet = None,

0 commit comments

Comments
 (0)