def make_example_graph(): in1, in2 = pyrtl.Input(8, 'in1'), pyrtl.Input(8, 'in2') out = pyrtl.Output(9, 'output') out <<= adders.kogge_stone(in1, in2) pyrtl.synthesize() pyrtl.optimize()
# tracking down bugs. However, PyRTL introduces some new complexities because # the place where functionality is defined (when you construct and operate # on PyRTL classes) is separate in time from where that functionality is executed # (i.e. during simulation). Thus, sometimes it hard to track down where a wire # might have come from, or what exactly it is doing. # In this example specifically, we will be building a circuit that adds up three values. # However, instead of building an add function ourselves or using the # built-in "+" function in PyRTL, we will instead use the Kogge-Stone adders # in RtlLib, the standard library for PyRTL. # building three inputs in1, in2, in3 = (pyrtl.Input(8, "in" + str(x)) for x in range(1, 4)) out = pyrtl.Output(10, "out") add1_out = adders.kogge_stone(in1, in2) add2_out = adders.kogge_stone(add1_out, in2) out <<= add2_out # The most basic way of debugging PyRTL is to connect a value to an output wire # and use the simulation to trace the output. A simple "print" statement doesn't work # because the values in the wires are not populated during *creation* time # If we want to check the result of the first addition, we can connect an output wire # to the result wire of the first adder debug_out = pyrtl.Output(9, "debug_out") debug_out <<= add1_out # now simulate the circuit. Let's create some random inputs to feed our adder.
def prng_xoroshiro128(bitwidth, load, req, seed=None): """ Builds a PRNG using the Xoroshiro128+ algorithm in hardware. :param bitwidth: the desired bitwidth of the random number :param load: one bit signal to load the seed into the prng :param req: one bit signal to request a random number :param seed: 128 bits WireVector, defaults to None (self-seeding), refrain from self-seeding if reseeding at run time is required :return ready, rand: ready is a one bit signal showing the random number has been produced, rand is a register containing the random number with the given bitwidth An efficient noncryptographic PRNG, has much smaller area than Trivium. But it does require a 64-bit adder to compute the output, so it is a bit slower. Has a period of 2**128 - 1. Passes most statistical tests. Outputs a 64-bit random word each cycle, takes multiple cycles if more than 64 bits are requested, and MSBs of the random words are returned if the bitwidth is not a multiple of 64. See also http://xoroshiro.di.unimi.it/ """ from math import ceil, log from pyrtl.rtllib import adders from pyrtl.rtllib.libutils import _shifted_reg_next as shift # for readability if seed is None: import random cryptogen = random.SystemRandom() seed = cryptogen.randrange( 1, 2**128) # seed itself if no seed signal is given seed = pyrtl.as_wires(seed, 128) s0, s1 = (pyrtl.Register(64) for i in range(2)) output = pyrtl.WireVector(64) # update internal states by xoring, rotating, and shifting _s1 = s0 ^ s1 s0_next = (shift(s0, 'l', 55) | shift(s0, 'r', 9)) ^ shift(_s1, 'l', 14) ^ _s1 s1_next = shift(_s1, 'l', 36) | shift(_s1, 'r', 28) output <<= adders.kogge_stone(s0, s1) gen_cycles = int(ceil(bitwidth / 64)) counter_bitwidth = int(ceil(log(gen_cycles, 2))) if gen_cycles > 1 else 1 rand = pyrtl.Register(gen_cycles * 64) counter = pyrtl.Register(counter_bitwidth, 'counter') gen_done = counter == gen_cycles - 1 state = pyrtl.Register(1) WAIT, GEN = (pyrtl.Const(x) for x in range(2)) with pyrtl.conditional_assignment: with load: s0.next |= seed[:64] s1.next |= seed[64:] state.next |= WAIT with req: counter.next |= 0 s0.next |= s0_next s1.next |= s1_next rand.next |= pyrtl.concat(rand, output) state.next |= GEN with state == GEN: with ~gen_done: counter.next |= counter + 1 s0.next |= s0_next s1.next |= s1_next rand.next |= pyrtl.concat(rand, output) ready = ~load & ~req & (state == GEN) & gen_done return ready, rand[-bitwidth:] # return MSBs because LSBs are less random
# tracking down bugs. However, PyRTL introduces some new complexities because # the place where functionality is defdined (when you construct and operate # on PyRTL classes) is seperate in time from where that functionalty is executed # (i.e. during siumation). Thus, sometimes it hard to track down where a wire # might have come from, or what exactly it is doing. # In this example specifically, we will be building a circuit that adds up three values. # However, instead of building an add function ourselves or using the # built-in "+" function in PyRTL, we will instead use the Kogge-Stone adders # in RtlLib, the standard library for PyRTL. # building three inputs in1, in2, in3 = (pyrtl.Input(8, "in" + str(x)) for x in range(1, 4)) out = pyrtl.Output(10, "out") add1_out = adders.kogge_stone(in1, in2) add2_out = adders.kogge_stone(add1_out, in2) out <<= add2_out # The most basic way of debugging PyRTL is to connect a value to an output wire # and use the simulation to trace the output. A simple "print" statement doesn't work # because the values in the wires are not populated during *creation* time # If we want to check the result of the first addition, we can connect an output wire # to the result wire of the first adder debug_out = pyrtl.Output(9, "debug_out") debug_out <<= add1_out # now simulate the circuit. Let's create some random inputs to feed our adder.