UCSBarchlab / PyRTL

A collection of classes providing simple hardware specification, simulation, tracing, and testing suitable for teaching and research. Simplicity, usability, clarity, and extensibility are the overarching goals, rather than performance or optimization.
http://ucsbarchlab.github.io/PyRTL
BSD 3-Clause "New" or "Revised" License
253 stars 76 forks source link

Fix constant propagation for unsynthesized blocks #407

Closed mdko closed 2 years ago

mdko commented 2 years ago

When pyrtl.optimize() is called on unsynthesized blocks (i.e. blocks with multibit wires and all the higher-level nets), pyrtl.synthesize() fails during constant propagation.

For example:

a = pyrtl.Const(0b0101, 4, name='a')
a_inv = ~a
pyrtl.probe(a_inv, 'a_inv')

pyrtl.optimize()

results in

Traceback (most recent call last):
  File "const-prop-test.py", line 20, in <module>
    pyrtl.optimize()
  File "/Users/michael/PyRTL/pyrtl/passes.py", line 53, in optimize
    constant_propagation(block, True)
  File "/Users/michael/PyRTL/pyrtl/passes.py", line 182, in constant_propagation
    _constant_prop_pass(block, silence_unexpected_net_warnings)
  File "/Users/michael/PyRTL/pyrtl/passes.py", line 269, in _constant_prop_pass
    constant_prop_check(a_net)
  File "/Users/michael/PyRTL/pyrtl/passes.py", line 261, in constant_prop_check
    replace_net_with_const(output)
  File "/Users/michael/PyRTL/pyrtl/passes.py", line 210, in replace_net_with_const
    new_const_wire = Const(bitwidth=1, val=const_val, block=block)
  File "/Users/michael/PyRTL/pyrtl/wire.py", line 626, in __init__
    num, bitwidth = infer_val_and_bitwidth(val, bitwidth, signed)
  File "/Users/michael/PyRTL/pyrtl/helperfuncs.py", line 663, in infer_val_and_bitwidth
    return _convert_int(rawinput, bitwidth, signed)
  File "/Users/michael/PyRTL/pyrtl/helperfuncs.py", line 703, in _convert_int
    raise PyrtlError('insufficient bits for negative number')
pyrtl.pyrtlexceptions.PyrtlError: insufficient bits for negative number

The reason is two-fold:

  1. The function being used to invert a constant number is incorrect (it was doing 1-x, but if you take the number 0b0101 (5) as an example, 1 - 5 = -4 = 0b1100 != 0b1010); please correct me if I'm wrong in my understanding of what was going on there.
  2. The size of the constant being created in its place was always 1 (i.e. it was assuming synthesis had been done), rather than the size of the destination wire it was being fed to.

This PR fixes both of those things and adds a unit test. I think more unit tests should be added to test the correctness of optimization in general, but for expediency's sake, I've added at least one here.