34
34
is a context manager (under which conditional assignements can be made), and "otherwise",
35
35
which is an instance that stands in for a 'fall through' case. The details of how these
36
36
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
38
64
39
65
In addition to the conditional context, there is a helper function "currently_under_condition"
40
66
which will test if the code where it is called is currently elaborating hardware
@@ -67,6 +93,13 @@ def currently_under_condition():
67
93
# instances (hopefully the only and unchanging instances) of the following two types.
68
94
69
95
class _ConditionalAssignment (object ):
96
+ def __init__ (self ):
97
+ self .defaults = {}
98
+
99
+ def __call__ (self , defaults ):
100
+ self .defaults = defaults
101
+ return self
102
+
70
103
""" Context providing funcitionality of "conditional_assignment". """
71
104
def __enter__ (self ):
72
105
global _depth
@@ -75,7 +108,7 @@ def __enter__(self):
75
108
76
109
def __exit__ (self , * exc_info ):
77
110
try :
78
- _finalize ()
111
+ _finalize (self . defaults )
79
112
finally :
80
113
# even if the above finalization throws an error we need to
81
114
# 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):
181
214
return True
182
215
183
216
184
- def _finalize ():
217
+ def _finalize (defaults ):
185
218
"""Build the required muxes and call back to WireVector to finalize the wirevector build."""
186
219
from .memory import MemBlock
187
220
from pyrtl .corecircuits import select
@@ -203,13 +236,13 @@ def _finalize():
203
236
# handle wirevector and register assignments
204
237
else :
205
238
if isinstance (lhs , Register ):
206
- if hasattr ( lhs , 'condition_default' ) :
207
- result = lhs . condition_default
239
+ if lhs in defaults :
240
+ result = defaults [ lhs ]
208
241
else :
209
242
result = lhs # default for registers is "self"
210
243
elif isinstance (lhs , WireVector ):
211
- if hasattr ( lhs , 'condition_default' ) :
212
- result = lhs . condition_default
244
+ if lhs in defaults :
245
+ result = defaults [ lhs ]
213
246
else :
214
247
result = 0 # default for wire is "0"
215
248
else :
0 commit comments