-
Notifications
You must be signed in to change notification settings - Fork 87
Description
It is easy to make confusing mistakes with signed values in PyRTL. A broken example:
import pyrtl
a = -128
b = 127
# Compute a - b, which should be -255.
a_const = pyrtl.Const(name='a', val=a, signed=True, bitwidth=8)
b_const = pyrtl.Const(name='b', val=b, signed=True, bitwidth=8)
diff = a_const - b_const
diff.name = 'diff'
sim = pyrtl.Simulation()
sim.step()
print('expected diff is', a - b)
print('actual diff is',
pyrtl.val_to_signed_integer(sim.inspect('diff'), bitwidth=diff.bitwidth),
'bitwidth', diff.bitwidth)
When this example is run, it produces:
expected diff is -255
actual diff is 1 bitwidth 9
Note that the actual output has bitwidth 9, which is sufficient to represent -255.
This example is especially confusing because PyRTL has all the information to do the right thing, because we've marked the Const
s as signed=True
. To fix this example, the user currently has to call signed_sub
instead of using the overloaded -
operator.
The current approach is error prone because the user has to keep track of signed-ness as values propagate through their circuit, and make sure they call functions like signed_add
and val_to_signed_integer
at the right times.
It seems better to track signed-ness within WireVector, or within a WireVector subclass, but this raises questions about type promotion - what happens when we add a signed value and an unsigned value? My current intuition is to match Verilog's promotion behavior, but we should think about this more.
See also #417