Helper Functions

Built-in Hardware Generators

Some useful hardware generators (e.g. muxes, signed multipliers, etc.)

pyrtl.corecircuits.mux(index, *mux_ins, **kwargs)

Multiplexer returning the value of the wire in .

Parameters:
  • index (WireVector) – used as the select input to the multiplexer
  • mux_ins (WireVector) – additional WireVector arguments selected when select>1
  • kwargs (WireVector) – additional WireVectors, keyword arg “default” If you are selecting between less items than your index can address, you can use the “default” keyword argument to auto-expand those terms. For example, if you have a 3-bit index but are selecting between 6 options, you need to specify a value for those other 2 possible values of index (0b110 and 0b111).
Returns:

WireVector of length of the longest input (not including select)

To avoid confusion, if you are using the mux where the select is a “predicate” (meaning something that you are checking the truth value of rather than using it as a number) it is recommended that you use the select function instead as named arguments because the ordering is different from the classic ternary operator of some languages.

Example of mux as “selector” to pick between a0 and a1:

index = WireVector(1)
mux( index, a0, a1 )

Example of mux as “selector” to pick between a0 … a3:

index = WireVector(2)
mux( index, a0, a1, a2, a3 )

Example of “default” to specify additional arguments:

index = WireVector(3)
mux( index, a0, a1, a2, a3, a4, a5, default=0 )
pyrtl.corecircuits.select(sel, truecase, falsecase)

Multiplexer returning falsecase for select==0, otherwise truecase.

Parameters:
  • sel (WireVector) – used as the select input to the multiplexer
  • falsecase (WireVector) – the WireVector selected if select==0
  • truecase (WireVector) – the WireVector selected if select==1

The hardware this generates is exactly the same as “mux” but by putting the true case as the first argument it matches more of the C-style ternary operator semantics which can be helpful for readablity.

Example of mux as “ternary operator” to take the max of ‘a’ and 5:

select( a<5, truecase=a, falsecase=5 )
pyrtl.corecircuits.concat(*args)

Concatenates multiple WireVectors into a single WireVector

Parameters:args (WireVector) – inputs to be concatenated
Returns:WireVector with length equal to the sum of the args’ lengths

You can provide multiple arguments and they will be combined with the right-most argument being the least significant bits of the result. Note that if you have a list of arguments to concat together you will likely want index 0 to be the least significant bit and so if you unpack the list into the arguements here it will be backwards. The function concat_list is provided for that case specifically.

Example using concat to combine two bytes into a 16-bit quantity:

concat( msb, lsb )
pyrtl.corecircuits.concat_list(wire_list)

Concatenates a list of WireVectors into a single WireVector

Parameters:wire_list – list of WireVectors to concat
Returns:WireVector with length equal to the sum of the args’ lengths

This take a list of wirevectors and concats them all into a single wire vector with the element at index 0 serving as the least significant bits. This is useful when you have a variable number of wirevectors to concatenate, otherwise “concat” is prefered.

Example using concat to combine two bytes into a 16-bit quantity:

mylist = [ lsb, msb ]
concat_list( mylist )
pyrtl.corecircuits.signed_add(a, b)

Return wirevector for result of signed addition.

Parameters:
  • a – a wirevector to serve as first input to addition
  • b – a wirevector to serve as second input to addition

Given a length n and length m wirevector the result of the signed addition is length max(n,m)+1. The inputs are twos complement sign extended to the same length before adding.

pyrtl.corecircuits.mult_signed(a, b)
pyrtl.corecircuits.signed_mult(a, b)

Return a*b where a and b are treated as signed values.

pyrtl.corecircuits.signed_lt(a, b)

Return a single bit result of signed less than comparison.

pyrtl.corecircuits.signed_le(a, b)

Return a single bit result of signed less than or equal comparison.

pyrtl.corecircuits.signed_gt(a, b)

Return a single bit result of signed greater than comparison.

pyrtl.corecircuits.signed_ge(a, b)

Return a single bit result of signed greater than or equal comparison.

pyrtl.corecircuits.shift_left_arithmetic(bits_to_shift, shift_amount)

Shift left arithmetic operation.

Parameters:
  • bits_to_shift – WireVector to shift left
  • shift_amount – WireVector specifying amount to shift
Returns:

WireVector of same length as bits_to_shift

This function returns a new WireVector of length equal to the length of the input bits_to_shift but where the bits have been shifted to the left. An arithemetic shift is one that treats the value as as signed number, although for left shift arithmetic and logic shift are identical. Note that shift_amount is treated as unsigned.

pyrtl.corecircuits.shift_right_arithmetic(bits_to_shift, shift_amount)

Shift right arithmetic operation.

Parameters:
  • bits_to_shift – WireVector to shift right
  • shift_amount – WireVector specifying amount to shift
Returns:

WireVector of same length as bits_to_shift

This function returns a new WireVector of length equal to the length of the input bits_to_shift but where the bits have been shifted to the right. An arithemetic shift is one that treats the value as as signed number, meaning the sign bit (the most significant bit of bits_to_shift) is shifted in. Note that shift_amount is treated as unsigned.

pyrtl.corecircuits.shift_left_logical(bits_to_shift, shift_amount)

Shift left logical operation.

Parameters:
  • bits_to_shift – WireVector to shift left
  • shift_amount – WireVector specifying amount to shift
Returns:

WireVector of same length as bits_to_shift

This function returns a new WireVector of length equal to the length of the input bits_to_shift but where the bits have been shifted to the left. An logical shift is one that treats the value as as unsigned number, meaning the zeros are shifted in. Note that shift_amount is treated as unsigned.

pyrtl.corecircuits.shift_right_logical(bits_to_shift, shift_amount)

Shift right logical operation.

Parameters:
  • bits_to_shift – WireVector to shift left
  • shift_amount – WireVector specifying amount to shift
Returns:

WireVector of same length as bits_to_shift

This function returns a new WireVector of length equal to the length of the input bits_to_shift but where the bits have been shifted to the right. An logical shift is one that treats the value as as unsigned number, meaning the zeros are shifted in regardless of the “sign bit”. Note that shift_amount is treated as unsigned.

pyrtl.corecircuits.match_bitwidth(*args, **opt)

Matches the bitwidth of all of the input arguments with zero or sign extend

Parameters:
  • args – WireVectors of which to match bitwidths
  • opt – Optional keyword argument ‘signed=True’ (defaults to False)
Returns:

tuple of args in order with extended bits

Example of matching the bitwidths of two WireVectors a and b with with zero extention:

a,b = match_bitwidth(a, b)

Example of matching the bitwidths of three WireVectors a,`b`, and c with with sign extention:

a,b = match_bitwidth(a, b, c, signed=True)
pyrtl.corecircuits.as_wires(val, bitwidth=None, truncating=True, block=None)

Return wires from val which may be wires, integers, strings, or bools.

Parameters:
  • val – a wirevector-like object or something that can be converted into a Const
  • bitwidth – The bitwidth the resulting wire should be
  • truncating (bool) – determines whether bits will be dropped to achieve the desired bitwidth if it is too long (if true, the most-significant bits will be dropped)
  • block (Block) – block to use for wire

This function is mainly used to coerce values into WireVectors (for example, operations such as “x+1” where “1” needs to be converted to a Const WireVector). An example:

def myhardware(input_a, input_b):
    a = as_wires(input_a)
    b = as_wires(input_b)
myhardware(3, x)

The function as_wires will covert the 3 to Const but keep x unchanged assuming it is a WireVector.

pyrtl.corecircuits.bitfield_update(w, range_start, range_end, newvalue, truncating=False)

Return wirevector w but with some of the bits overwritten by newvalue.

Parameters:
  • w – a wirevector to use as the starting point for the update
  • range_start – the start of the range of bits to be updated
  • range_end – the end of the range of bits to be updated
  • newvalue – the value to be written in to the start:end range
  • truncating – if true, clip the newvalue to be the proper number of bits

Given a wirevector w, this function returns a new wirevector that is identical to w except in the range of bits specified. In that specified range, the value newvalue is swapped in. For example: bitfield_update(w, 20, 23, 0x7) will return return a wirevector of the same length as w, and with the same values as w, but with bits 20, 21, and 22 all set to 1.

Note that range_start and range_end will be inputs to a slice and so standar Python slicing rules apply (e.g. negative values for end-relative indexing and support for None).

w = bitfield_update(w, 20, 23, 0x7)  # sets bits 20, 21, 22 to 1
w = bitfield_update(w, 20, 23, 0x6)  # sets bit 20 to 0, bits 21 and 22 to 1
w = bitfield_update(w, 20, None, 0x7)  # assuming w is 32 bits, sets bits 31..20 = 0x7
w = bitfield_update(w, -1, None, 0x1)  # set the LSB (bit) to 1
pyrtl.corecircuits.enum_mux(cntrl, table, default=None, strict=True)

Build a mux for the control signals specified by an enum.

Parameters:
  • cntrl – is a wirevector and control for the mux.
  • table – is a dictionary of the form mapping enum->wirevector.
  • default – is a wirevector to use when the key is not present. In addtion it is possible to use the key ‘otherwise’ to specify a default value, but it is an error if both are supplied.
  • strict – is flag, that when set, will cause enum_mux to check that the dictionary has an entry for every possible value in the enum. Note that if a default is set, then this check is not performed as the default will provide valid values for any underspecified keys.
Returns:

a wirevector which is the result of the mux.

class Command(Enum):
    ADD = 1
    SUB = 2
enum_mux(cntrl, {ADD: a+b, SUB: a-b})
enum_mux(cntrl, {ADD: a+b}, strict=False)  # SUB case undefined
enum_mux(cntrl, {ADD: a+b, otherwise: a-b})
enum_mux(cntrl, {ADD: a+b}, default=a-b)
pyrtl.corecircuits.and_all_bits(vector)

Returns WireVector, the result of “and”ing all items of the argument vector.

Takes a single WireVector and returns a 1 bit result, the bitwise and of all of the bits in the vector to a single bit.

pyrtl.corecircuits.or_all_bits(vector)

Returns WireVector, the result of “or”ing all items of the argument vector.

Takes a single WireVector and returns a 1 bit result, the bitwise or of all of the bits in the vector to a single bit.

pyrtl.corecircuits.xor_all_bits(vector)

Returns WireVector, the result of “xor”ing all items of the argument vector.

Takes a single WireVector and returns a 1 bit result, the bitwise xor of all of the bits in the vector to a single bit. This function is also aliased as parity and you can call it either way.

pyrtl.corecircuits.parity(vector)

Returns WireVector, the result of “xor”ing all items of the argument vector.

Takes a single WireVector and returns a 1 bit result, the bitwise xor of all of the bits in the vector to a single bit. This function is also aliased as parity and you can call it either way.

pyrtl.corecircuits.tree_reduce(op, vector)
pyrtl.corecircuits.rtl_any(*vectorlist)

Hardware equivalent of python native “any”.

Parameters:vectorlist (WireVector) – all arguments are WireVectors of length 1
Returns:WireVector of length 1

Returns a 1-bit WireVector which will hold a ‘1’ if any of the inputs are ‘1’ (i.e. it is a big ol’ OR gate)

pyrtl.corecircuits.rtl_all(*vectorlist)

Hardware equivalent of python native “all”.

Parameters:vectorlist (WireVector) – all arguments are WireVectors of length 1
Returns:WireVector of length 1

Returns a 1-bit WireVector which will hold a ‘1’ only if all of the inputs are ‘1’ (i.e. it is a big ol’ AND gate)

Hardware Design Helper Functions

Helper functions that make constructing hardware easier.

pyrtl.helperfuncs.probe(w, name=None)

Print useful information about a WireVector when in debug mode.

Parameters:
  • w – WireVector from which to get info
  • name – optional name for probe (defaults to an autogenerated name)
Returns:

original WireVector w

Probe can be inserted into a existing design easily as it returns the original wire unmodified. For example y <<= x[0:3] + 4 could be turned into y <<= probe(x)[0:3] + 4 to give visibility into both the origin of x (including the line that WireVector was originally created) and the run-time values of x (which will be named and thus show up by default in a trace. Likewise y <<= probe(x[0:3]) + 4, y <<= probe(x[0:3] + 4), and probe(y) <<= x[0:3] + 4 are all valid uses of probe.

Note: probe does actually add a wire to the working block of w (which can confuse various post-processing transforms such as output to verilog).

pyrtl.helperfuncs.rtl_assert(w, exp, block=None)

Add hardware assertions to be checked on the RTL design.

Parameters:
  • w – should be a WireVector
  • exp (Exception) – Exception to throw when assertion fails
  • block (Block) – block to which the assertion should be added (default to working block)
Returns:

the Output wire for the assertion (can be ignored in most cases)

If at any time during execution the wire w is not true (i.e. asserted low) then simulation will raise exp.

pyrtl.helperfuncs.check_rtl_assertions(sim)

Checks the values in sim to see if any registers assertions fail.

Parameters:sim – Simulation in which to check the assertions
Returns:None
pyrtl.helperfuncs.input_list(names, bitwidth=None)

Allocate and return a list of Inputs.

Parameters:
  • names – Names for the Inputs. Can be a list or single comma/space-separated string
  • bitwidth – The desired bitwidth for the resulting Inputs.
Returns:

List of Inputs.

Equivalent to:

wirevector_list(names, bitwidth, wvtype=pyrtl.wire.Input)
pyrtl.helperfuncs.output_list(names, bitwidth=None)

Allocate and return a list of Outputs.

Parameters:
  • names – Names for the Outputs. Can be a list or single comma/space-separated string
  • bitwidth – The desired bitwidth for the resulting Outputs.
Returns:

List of Outputs.

Equivalent to:

wirevector_list(names, bitwidth, wvtype=pyrtl.wire.Output)
pyrtl.helperfuncs.register_list(names, bitwidth=None)

Allocate and return a list of Registers.

Parameters:
  • names – Names for the Registers. Can be a list or single comma/space-separated string
  • bitwidth – The desired bitwidth for the resulting Registers.
Returns:

List of Registers.

Equivalent to:

wirevector_list(names, bitwidth, wvtype=pyrtl.wire.Register)
pyrtl.helperfuncs.wirevector_list(names, bitwidth=None, wvtype=<class 'pyrtl.wire.WireVector'>)

Allocate and return a list of WireVectors.

Parameters:
  • names – Names for the WireVectors. Can be a list or single comma/space-separated string
  • bitwidth – The desired bitwidth for the resulting WireVectors.
  • wvtype (WireVector) – Which WireVector type to create.
Returns:

List of WireVectors.

Additionally, the names string can also contain an additional bitwidth specification separated by a / in the name. This cannot be used in combination with a bitwidth value other than 1.

Examples:

wirevector_list(['name1', 'name2', 'name3'])
wirevector_list('name1, name2, name3')
wirevector_list('input1 input2 input3', bitwidth=8, wvtype=pyrtl.wire.Input)
wirevector_list('output1, output2 output3', bitwidth=3, wvtype=pyrtl.wire.Output)
wirevector_list('two_bits/2, four_bits/4, eight_bits/8')
wirevector_list(['name1', 'name2', 'name3'], bitwidth=[2, 4, 8])
pyrtl.helperfuncs.val_to_signed_integer(value, bitwidth)

Return value as intrepreted as a signed integer under twos complement.

Parameters:
  • value – a python integer holding the value to convert
  • bitwidth – the length of the integer in bits to assume for conversion

Given an unsigned integer (not a wirevector!) covert that to a signed integer. This is useful for printing and interpreting values which are negative numbers in twos complement.

val_to_signed_integer(0xff, 8) == -1
pyrtl.helperfuncs.formatted_str_to_val(data, format, enum_set=None)

Return an unsigned integer representation of the data given format specified.

Parameters:
  • data – a string holding the value to convert
  • format – a string holding a format which will be used to convert the data string
  • enum_set – an iterable of enums which are used as part of the converstion process

Given a string (not a wirevector!) covert that to an unsigned integer ready for input to the simulation enviornment. This helps deal with signed/unsigned numbers (simulation assumes the values have been converted via two’s complement already), but it also takes hex, binary, and enum types as inputs. It is easiest to see how it works with some examples.

formatted_str_to_val('2', 's3') == 2  # 0b010
formatted_str_to_val('-1', 's3') == 7  # 0b111
formatted_str_to_val('101', 'b3') == 5
formatted_str_to_val('5', 'u3') == 5
formatted_str_to_val('-3', 's3') == 5
formatted_str_to_val('a', 'x3') == 10
class Ctl(Enum):
    ADD = 5
    SUB = 12
formatted_str_to_val('ADD', 'e3/Ctl', [Ctl]) == 5
formatted_str_to_val('SUB', 'e3/Ctl', [Ctl]) == 12
pyrtl.helperfuncs.val_to_formatted_str(val, format, enum_set=None)

Return a string representation of the value given format specified.

Parameters:
  • val – a string holding an unsigned integer to convert
  • format – a string holding a format which will be used to convert the data string
  • enum_set – an iterable of enums which are used as part of the converstion process

Given an unsigned integer (not a wirevector!) covert that to a strong ready for output to a human to interpret. This helps deal with signed/unsigned numbers (simulation operates on values that have been converted via two’s complement), but it also generates hex, binary, and enum types as outputs. It is easiest to see how it works with some examples.

formatted_str_to_val(2, 's3') == '2'
formatted_str_to_val(7, 's3') == '-1'
formatted_str_to_val(5, 'b3') == '101'
formatted_str_to_val(5, 'u3') == '5'
formatted_str_to_val(5, 's3') == '-3'
formatted_str_to_val(10, 'x3') == 'a'
class Ctl(Enum):
    ADD = 5
    SUB = 12
formatted_str_to_val('ADD', 'e3/Ctl', [Ctl]) == 5
formatted_str_to_val('SUB', 'e3/Ctl', [Ctl]) == 12
pyrtl.helperfuncs.get_stacks(*wires)
pyrtl.helperfuncs.get_stack(wire)
pyrtl.helperfuncs.find_loop(block=None)
pyrtl.helperfuncs.find_and_print_loop(block=None)
pyrtl.helperfuncs.print_loop(loop_data)