Skip to content

Commit e295dd0

Browse files
committed
Remove references to the analysis and conditional submodules.
This moves most of the conditional assignment documentation from `conditional.py` to `basic.rst` because `conditional_assignment` and `otherwise` are globals, and Sphinx can't look up the docstrings for aliased globals.
1 parent b8dafaa commit e295dd0

4 files changed

Lines changed: 260 additions & 247 deletions

File tree

docs/analysis.rst

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,18 @@ Tools for analyzing and optimizing aspects of PyRTL designs.
99
Estimation
1010
----------
1111

12-
.. automodule:: pyrtl.analysis
12+
.. autoclass:: pyrtl.TimingAnalysis
1313
:members:
1414
:special-members: __init__
1515

16+
.. autofunction:: pyrtl.area_estimation
17+
.. autofunction:: pyrtl.distance
18+
.. autofunction:: pyrtl.fanout
19+
.. autoclass:: pyrtl.analysis.PathsResult
20+
:members:
21+
.. autofunction:: pyrtl.paths
22+
.. autofunction:: pyrtl.yosys_area_delay
23+
1624
Optimization
1725
------------
1826

docs/basic.rst

Lines changed: 176 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,5 +61,179 @@ Constants
6161
Conditional Assignment
6262
----------------------
6363

64-
.. automodule:: pyrtl.conditional
65-
:members:
64+
.. autodata:: pyrtl.conditional_assignment
65+
66+
:class:`WireVectors<.WireVector>`, :class:`Registers<.Register>`, and
67+
:class:`MemBlocks<.MemBlock>` can be conditionally assigned values based on predicates.
68+
69+
Conditional assignments are written with `Python with statements
70+
<https://docs.python.org/3/reference/compound_stmts.html#with>`_, using two
71+
context managers:
72+
73+
#. :data:`.conditional_assignment`, which provides the framework for specifying
74+
conditional assignments.
75+
#. :data:`.otherwise`, which specifies the 'fall through' case.
76+
77+
Conditional assignments are easiest to understand with an example::
78+
79+
r1 = pyrtl.Register(bitwidth=8)
80+
r2 = pyrtl.Register(bitwidth=8)
81+
w = pyrtl.WireVector(bitwidth=8)
82+
mem = pyrtl.MemBlock(bitwidth=8, addrwidth=4)
83+
84+
a = pyrtl.Input(bitwidth=1)
85+
b = pyrtl.Input(bitwidth=1)
86+
c = pyrtl.Input(bitwidth=1)
87+
d = pyrtl.Input(bitwidth=1)
88+
89+
with pyrtl.conditional_assignment:
90+
with a:
91+
# Set when a is True.
92+
r1.next |= 1
93+
mem[0] |= 2
94+
with b:
95+
# Set when a and b are both True.
96+
r2.next |= 3
97+
with c:
98+
# Set when a is False and c is True.
99+
r1.next |= 4
100+
r2.next |= 5
101+
with pyrtl.otherwise:
102+
# Set when a and c are both False.
103+
r2.next |= 6
104+
105+
with d:
106+
# Set when d is True. A `with` block after an `otherwise` starts a new
107+
# set of conditional assignments.
108+
w |= 7
109+
110+
This :data:`.conditional_assignment` is equivalent to::
111+
112+
r1.next <<= pyrtl.select(a, 1, pyrtl.select(c, 4, r1))
113+
r2.next <<= pyrtl.select(a, pyrtl.select(b, 3, r2), pyrtl.select(c, 5, 6))
114+
w <<= pyrtl.select(d, 7, 0)
115+
mem[0] <<= pyrtl.MemBlock.EnabledWrite(data=2, enable=a)
116+
117+
Conditional assignments are generally recommended over nested :func:`.select` statements
118+
because conditional assignments are easier to read and write.
119+
120+
:data:`.conditional_assignment` accepts an optional ``default`` argument that
121+
maps from :class:`.WireVector` to its default value for the
122+
:data:`.conditional_assignment` block. ``defaults`` are not supported for
123+
:class:`.MemBlock`. See :ref:`conditional_assignment_defaults` for more details.
124+
125+
See `the state machine example
126+
<https://github.com/UCSBarchlab/PyRTL/blob/development/examples/example3-statemachine.py>`_
127+
for more examples of :data:`.conditional_assignment`.
128+
129+
.. autodata:: pyrtl.otherwise
130+
131+
Context manager implementing PyRTL's ``otherwise`` under :data:`.conditional_assignment`.
132+
133+
.. autofunction:: pyrtl.currently_under_condition
134+
135+
.. _conditional_assignment_defaults:
136+
137+
-------------------------------
138+
Conditional Assignment Defaults
139+
-------------------------------
140+
141+
Every PyRTL wire, register, and memory must have a value in every cycle. PyRTL does not
142+
support "don't care" or ``X`` values. To satisfy this requirement, conditional
143+
assignment must always assign a value to every wire in a :data:`.conditional_assignment`
144+
block, even if the :data:`.conditional_assignment` does not specify a value. This can
145+
happen when:
146+
147+
#. A condition is ``True``, but no value is specified for a wire or register in that
148+
condition's ``with`` block. In the example above, no value is specified for ``r1`` in
149+
the :data:`.otherwise` block.
150+
#. No conditions are ``True``, and there is no :data:`.otherwise` block. In the example
151+
above, there is no :data:`.otherwise` block to for the case when ``d`` is ``False``,
152+
so no value is specified for ``w`` when ``d`` is ``False``.
153+
154+
When this happens for a wire, ``0`` is assigned as a default value. See how a ``0``
155+
appears in the final :func:`.select` in the equivalent example above.
156+
157+
When this happens for a register, the register's current value is assigned as a default
158+
value. See how ``r1`` and ``r2`` appear within the :func:`.select` s in the first and second
159+
lines of the example above.
160+
161+
When this happens for a memory, the memory's write port is disabled. See how the example
162+
above uses a :class:`.EnabledWrite` to disable writes to ``mem[0]`` when ``a`` is
163+
``False``.
164+
165+
These default values can be changed by passing a ``defaults`` dict to
166+
:data:`.conditional_assignment`, as seen in this example::
167+
168+
# Most instructions advance the program counter (`pc`) by one instruction. A few
169+
# instructions change `pc` in special ways.
170+
pc = pyrtl.Register(bitwidth=32)
171+
instr = pyrtl.WireVector(bitwidth=32)
172+
res = pyrtl.WireVector(bitwidth=32)
173+
174+
op = instr[:7]
175+
ADD = 0b0110011
176+
JMP = 0b1101111
177+
178+
# Use conditional_assignment's `defaults` to advance `pc` by one instruction by
179+
# default.
180+
with pyrtl.conditional_assignment(defaults={pc: pc + 1}):
181+
with op == ADD:
182+
res |= instr[15:20] + instr[20:25]
183+
# pc.next will be updated to pc + 1
184+
with op == JMP:
185+
pc.next |= pc + instr[7:]
186+
# res will be set to 0
187+
188+
.. WARNING::
189+
:data:`.conditional_assignment` ``defaults`` are not supported for
190+
:class:`.MemBlock`.
191+
192+
-------------------------------------------
193+
The Conditional Assigment Operator (``|=``)
194+
-------------------------------------------
195+
196+
Conditional assignments are written with the ``|=`` operator, and not the usual ``<<=``
197+
operator.
198+
199+
* The ``|=`` operator is a *conditional* assignment. Conditional assignments can only be
200+
written in a :data:`.conditional_assignment` block.
201+
* The ``<<=`` operator is an *unconditional* assignment, *even if* it is written in a
202+
:data:`.conditional_assignment` block.
203+
204+
Consider this example::
205+
206+
w1 = pyrtl.WireVector()
207+
w2 = pyrtl.WireVector()
208+
with pyrtl.conditional_assignment:
209+
with a:
210+
w1 |= 1
211+
w2 <<= 2
212+
213+
Which is equivalent to::
214+
215+
w1 <<= pyrtl.select(a, 1, 0)
216+
w2 <<= 2
217+
218+
This behavior may seem undesirable, but consider this example::
219+
220+
def make_adder(x: pyrtl.WireVector) -> pyrtl.WireVector:
221+
output = pyrtl.WireVector(bitwidth=a.bitwidth + 1)
222+
output <<= x + 2
223+
return output
224+
225+
w = pyrtl.WireVector()
226+
with pyrtl.conditional_assignment:
227+
with a:
228+
w |= make_adder(b)
229+
230+
Which is equivalent to::
231+
232+
# The assignment to `output` in `make_adder` is unconditional.
233+
w <<= pyrtl.select(a, make_adder(b), 0)
234+
235+
In this example the ``<<=`` in ``make_adder`` should be unconditional, even though
236+
``make_adder`` is called from a :data:`.conditional_assignment`, because the top-level
237+
assignment to ``w`` is already conditional. Making the lower-level assignment to
238+
``output`` conditional would not make sense, especially if ``output`` is used elsewhere
239+
in the circuit.

0 commit comments

Comments
 (0)