import pyrtl

pyrtl.reset_working_block()

"""
Explanation: Example 2:  A Counter with Ripple Carry Adder.
This next example shows how you make stateful things with registers
and more complex hardware structures with functions.  We generate
a 3-bit ripple carry adder building off of the 1-bit adder from
the prior example, and then hook it to a register to count up modulo 8.
End of explanation
"""


def one_bit_add(a, b, carry_in):
    assert len(a) == len(b) == 1  # len returns the bitwidth
    sum = a ^ b ^ carry_in
    carry_out = a & b | a & carry_in | b & carry_in
    return sum, carry_out

"""
Explanation: A function in PyRTL is nothing special -- it just so happens that the statements
it encapsulate tell PyRTL to build some hardware.
End of explanation
"""


def ripple_add(a, b, carry_in=0):
    a, b = pyrtl.match_bitwidth(a, b)
    # this function is a function that allows us to match the bitwidth of multiple
    # different wires. By default, it zero extends the shorter bits
    if len(a) == 1:
        sumbits, carry_out = one_bit_add(a, b, carry_in)
    else:
        lsbit, ripplecarry = one_bit_add(a[0], b[0], carry_in)
        msbits, carry_out = ripple_add(a[1:], b[1:], ripplecarry)
        sumbits = pyrtl.concat(msbits, lsbit)
    return sumbits, carry_out

"""
Explanation: If we call one_bit_add
above with the arguments x, y, and z it will make a one-bit adder to add
those values together and return the wires for sum and carry_out as applied to x,
y, and z.  If I call it again on i, j, and k it will build a new one-bit
adder for those inputs and return the resulting sum and carry_out for that adder.
While PyRTL actually provides an "+" operator for wirevectors which generates
adders, a ripple carry adder is something people can understand easily but has
enough structure to be mildly interesting.  Let's define an adder of arbitrary
length recursively and (hopefully) pythonically.  More comments after the code.
End of explanation
"""


counter = pyrtl.Register(bitwidth=3, name='counter')
sum, carry_out = ripple_add(counter, pyrtl.Const("1'b1"))
counter.next <<= sum

"""
Explanation: The above code breaks down into two cases:

If the size of the inputs is one-bit just do one_bit_add.
if they are more than one bit, do a one-bit add on the least significant bits, a ripple carry on the rest, and then stick the results back together into one WireVector.

A couple interesting features of PyRTL can be seen here:

WireVectors can be indexed like lists, with [0] accessing the least significant bit and [1:] being an example of the use of Python slicing syntax.
While you can add two lists together in python a WireVector + Wirevector means "make an adder" so to concatenate the bits of two vectors one need to use "concat".
If we look at "cin" it seems to have a default value of the integer "0" but is a WireVector at other times.Python supports polymorphism throughout and PyRTL will cast integers and some other types to WireVectors when it can.

Now let's build a 3-bit counter from our N-bit ripple carry adder.
End of explanation
"""


sim_trace = pyrtl.SimulationTrace()
sim = pyrtl.Simulation(tracer=sim_trace)
for cycle in range(15):
    sim.step({})
    assert sim.value[counter] == cycle % 8
sim_trace.render_trace()

"""
Explanation: A couple new things in the above code:

The two remaining types of basic WireVectors, Const and Register, both  appear.  Const, unsurprisingly, is just for holding constants (such as the 0 in ripple_add), but here we create one directly from a Verilog-like string which includes both the value and the bitwidth.
Registers are just like wires, except their updates are delayed to the next clock cycle.  This is made explicit in the syntax through the property '.next' which should always be set for registers.
In this simple example, we take counter next cycle equal to counter this cycle plus one.

Now let's run the bugger.  No need for inputs, it doesn't have any, but let's
throw in an assert to check that it really counts up modulo 8.  Finally we'll
print the trace to the screen.
End of explanation
"""


