Skip to content

Commit 4e82a69

Browse files
authored
Merge pull request #350 from pllab/cond-update-defaults
A different way of specifying conditional defaults
2 parents e00bb40 + 54699a3 commit 4e82a69

File tree

2 files changed

+77
-7
lines changed

2 files changed

+77
-7
lines changed

pyrtl/conditional.py

Lines changed: 40 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,33 @@
3434
is a context manager (under which conditional assignements can be made), and "otherwise",
3535
which is an instance that stands in for a 'fall through' case. The details of how these
3636
should be used, and the difference between normal assignments and condtional assignments,
37-
described in more detail in the state machine example from in prytl/examples.
37+
described in more detail in the state machine example in examples/example3-statemachine.py.
38+
39+
There are instances where you might want a wirevector to be set to a certain value in all
40+
but certain with blocks. For example, say you have a processor with a PC register that is
41+
normally updated to PC + 1 after each cycle, except when the current instruction is
42+
a branch or jump. You could represent that as follows::
43+
44+
pc = pyrtl.Register(32)
45+
instr = pyrtl.WireVector(32)
46+
res = pyrtl.WireVector(32)
47+
48+
op = instr[:7]
49+
ADD = 0b0110011
50+
JMP = 0b1101111
51+
52+
with conditional_assignment(
53+
defaults={
54+
pc: pc + 1,
55+
res: 0
56+
}
57+
):
58+
with op == ADD:
59+
res |= instr[15:20] + instr[20:25]
60+
# pc will be updated to pc + 1
61+
with op == JMP:
62+
pc.next |= pc + instr[7:]
63+
# res will be set to 0
3864
3965
In addition to the conditional context, there is a helper function "currently_under_condition"
4066
which will test if the code where it is called is currently elaborating hardware
@@ -67,6 +93,13 @@ def currently_under_condition():
6793
# instances (hopefully the only and unchanging instances) of the following two types.
6894

6995
class _ConditionalAssignment(object):
96+
def __init__(self):
97+
self.defaults = {}
98+
99+
def __call__(self, defaults):
100+
self.defaults = defaults
101+
return self
102+
70103
""" Context providing funcitionality of "conditional_assignment". """
71104
def __enter__(self):
72105
global _depth
@@ -75,7 +108,7 @@ def __enter__(self):
75108

76109
def __exit__(self, *exc_info):
77110
try:
78-
_finalize()
111+
_finalize(self.defaults)
79112
finally:
80113
# even if the above finalization throws an error we need to
81114
# reset the state to prevent errors from bleeding over
@@ -181,7 +214,7 @@ def _pred_sets_are_in_conflict(pred_set_a, pred_set_b):
181214
return True
182215

183216

184-
def _finalize():
217+
def _finalize(defaults):
185218
"""Build the required muxes and call back to WireVector to finalize the wirevector build."""
186219
from .memory import MemBlock
187220
from pyrtl.corecircuits import select
@@ -203,13 +236,13 @@ def _finalize():
203236
# handle wirevector and register assignments
204237
else:
205238
if isinstance(lhs, Register):
206-
if hasattr(lhs, 'condition_default'):
207-
result = lhs.condition_default
239+
if lhs in defaults:
240+
result = defaults[lhs]
208241
else:
209242
result = lhs # default for registers is "self"
210243
elif isinstance(lhs, WireVector):
211-
if hasattr(lhs, 'condition_default'):
212-
result = lhs.condition_default
244+
if lhs in defaults:
245+
result = defaults[lhs]
213246
else:
214247
result = 0 # default for wire is "0"
215248
else:

tests/test_conditional.py

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,43 @@ def test_two_signals_under_default_condition(self):
121121
r2.next |= 3
122122
self.check_trace(' i 01230123\nr1 01222344\nr2 00013334\n')
123123

124+
def test_default_value_for_wires(self):
125+
i = pyrtl.Register(bitwidth=2, name='i')
126+
i.next <<= i + 1
127+
r1 = pyrtl.Register(bitwidth=3, name='r1')
128+
r2 = pyrtl.Register(bitwidth=3, name='r2')
129+
r3 = pyrtl.Register(bitwidth=3, name='r3')
130+
o = pyrtl.Output(bitwidth=3, name='o')
131+
with pyrtl.conditional_assignment(
132+
defaults={
133+
r1: r1 + 2,
134+
r2: 6,
135+
o: 3
136+
}
137+
):
138+
with i < 2:
139+
r1.next |= r1 + 1
140+
# r2 will be updated to 6
141+
# r3 remains the same as previous cycle
142+
o |= i
143+
with i < 3:
144+
# r1 will be updated to r1 + 2
145+
r2.next |= r2 + 1
146+
# r3 remains the same as previous cycle
147+
# o will be updated to 3
148+
with pyrtl.otherwise:
149+
# r1 will be updated to r1 + 2
150+
# r2 will be updated to 6
151+
r3.next |= 2
152+
o |= 7
153+
self.check_trace(
154+
' i 01230123\n'
155+
' o 01370137\n'
156+
'r1 01246702\n'
157+
'r2 06676667\n'
158+
'r3 00002222\n'
159+
)
160+
124161
def test_error_on_unconditioned_update_in_under_conditional(self):
125162
i = pyrtl.Register(bitwidth=2, name='i')
126163
with pyrtl.conditional_assignment:

0 commit comments

Comments
 (0)