Helper Functions#

Cutting and Extending WireVectors#

The functions below provide ways of combining, slicing, and extending WireVectors in ways that are often useful in hardware design. The functions below extend those member functions of the WireVector class itself (which provides support for the Python builtin len, slicing e.g. wire[3:6], zero_extended(), sign_extended(), and many operators such as addition and multiplication).

pyrtl.corecircuits.concat(*args)[source]#

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 arguments 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)[source]#

Concatenates a list of WireVectors into a single WireVector.

Parameters:

wire_list (list[WireVector]) – 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 WireVector 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_list to combine two bytes into a 16-bit quantity:

mylist = [lsb, msb]
concat_list(mylist)
pyrtl.corecircuits.match_bitwidth(*args, **opt)[source]#

Matches the argument wires’ bitwidth via zero or sign extension, returning new WireVectors

Parameters:
  • args (WireVector) – 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 zero extension:

a, b = match_bitwidth(a, b)

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

a, b, c = match_bitwidth(a, b, c, signed=True)
pyrtl.helperfuncs.truncate(wirevector_or_integer, bitwidth)[source]#

Returns a WireVector or integer truncated to the specified bitwidth

Parameters:
  • wirevector_or_integer – Either a WireVector or an integer to be truncated.

  • bitwidth (int) – The length to which the first argument should be truncated.

Returns:

A truncated WireVector or integer as appropriate.

This function truncates the most significant bits of the input, leaving a result that is only bitwidth bits wide. For integers this is performed with a simple bitmask of size bitwidth. For WireVectors the function calls WireVector.truncate() and returns a WireVector of the specified bitwidth.

Examples:

truncate(9,3)  # returns 1  (0b1001 truncates to 0b001)
truncate(5,3)  # returns 5  (0b101 truncates to 0b101)
truncate(-1,3)  # returns 7  (-0b1 truncates to 0b111)
y = truncate(x+1, x.bitwidth)  # y.bitwdith will equal x.bitwidth
pyrtl.helperfuncs.chop(w, *segment_widths)[source]#

Returns a list of WireVectors, each a slice of the original w.

Parameters:
  • w (WireVector) – The WireVector to be chopped up into segments

  • segment_widths (int) – Additional arguments are integers which are bitwidths

Returns:

A list of WireVectors each with a proper segment width

Return type:

List[WireVector]

This function chops a WireVector into a set of smaller WireVectors of different lengths. It is most useful when multiple “fields” are contained with a single WireVector, for example when breaking apart an instruction. For example, if you wish to break apart a 32-bit MIPS I-type (Immediate) instruction you know it has an 6-bit opcode, 2 5-bit operands, and 16-bit offset. You could take each of those slices in absolute terms: offset=instr[0:16], rt=instr[16:21] and so on, but then you have to do the arithmetic yourself. With this function you can do all the fields at once which can be seen in the examples below.

As a check, chop will throw an error if the sum of the lengths of the fields given is not the same as the length of the WireVector to chop. Note also that chop assumes that the “rightmost” arguments are the least signficant bits (just like concat()) which is normal for hardware functions but makes the list order a little counter intuitive.

Examples:

opcode, rs, rt, offset = chop(instr, 6, 5, 5, 16)  # MIPS I-type instruction
opcode, instr_index = chop(instr, 6, 26)  # MIPS J-type instruction
opcode, rs, rt, rd, sa, function = chop(instr, 6, 5, 5, 5, 5, 6)  # MIPS R-type
msb, middle, lsb = chop(data, 1, 30, 1) # break out the most and least significant bits

Coercion to WireVector#

In PyRTL there is only one function in charge of coercing values into WireVectors, and that is as_wires(). This function is called in almost all helper functions and classes to manage the mixture of constants and WireVectors that naturally occur in hardware development.

pyrtl.corecircuits.as_wires(val, bitwidth=None, truncating=True, block=None)[source]#

Return wires from val which may be wires, integers (including IntEnums), strings, or bools.

Parameters:
  • val – a WireVector-like object or something that can be converted into a Const

  • bitwidth (int) – 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)

as_wires() will convert the 3 to Const but keep x unchanged assuming it is a WireVector.

Control Flow Hardware#

pyrtl.corecircuits.mux(index, *mux_ins, **kwargs)[source]#

Multiplexer returning the value of the wire from mux_ins according to index.

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 fewer 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 index)

To avoid confusion, if you are using the mux where the index 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 select() instead as named arguments because the ordering is different from the classic ternary operator of some languages.

Example of multiplexing between a0 and a1:

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

Example of multiplexing between a0, a1, a2, 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)[source]#

Multiplexer returning falsecase when sel == 0, otherwise truecase.

Parameters:
  • sel (WireVector) – used as the select input to the multiplexer

  • truecase (WireVector) – the WireVector selected if sel == 1

  • falsecase (WireVector) – the WireVector selected if sel == 0

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 readability.

Example of taking the min of a and 5:

select(a < 5, truecase=a, falsecase=5)
pyrtl.corecircuits.enum_mux(cntrl, table, default=None, strict=True)[source]#

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 to WireVector.

  • default – is a WireVector to use when the key is not present. In addition it is possible to use the key otherwise to specify a default value, but it is an error if both are supplied.

  • strict (bool) – when True, 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.

Examples:

from enum import IntEnum

class Command(IntEnum):
    ADD = 1
    SUB = 2
enum_mux(cntrl, {Command.ADD: a + b, Command.SUB: a - b})
enum_mux(cntrl, {Command.ADD: a + b}, strict=False)  # SUB case undefined
enum_mux(cntrl, {Command.ADD: a + b, otherwise: a - b})
enum_mux(cntrl, {Command.ADD: a + b}, default=a - b)
pyrtl.corecircuits.bitfield_update(w, range_start, range_end, newvalue, truncating=False)[source]#

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

Parameters:
  • w (WireVector) – a WireVector to use as the starting point for the update

  • range_start (int) – the start of the range of bits to be updated

  • range_end (int) – the end of the range of bits to be updated

  • newvalue (int) – the value to be written in to the start:end range

  • truncating (bool) – if true, silently clip newvalue to the proper bitwidth rather than throw an error if the value provided is too large

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 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 standard 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 MSB (bit) to 1
w = bitfield_update(w, None, -1, 0x9)  # set the bits before the MSB (bit) to 9
w = bitfield_update(w, None, 1, 0x1)  # set the LSB (bit) to 1
w = bitfield_update(w, 1, None, 0x9)  # set the bits after the LSB (bit) to 9
pyrtl.corecircuits.bitfield_update_set(w, update_set, truncating=False)[source]#

Return WireVector w but with some of the bits overwritten by values in update_set.

Parameters:
  • w (WireVector) – a WireVector to use as the starting point for the update

  • update_set – a map from tuples of integers (bit ranges) to the new values

  • truncating (bool) – if true, silently clip new values to the proper bitwidth rather than throw an error if the value provided is too large

Given a WireVector w, this function returns a new WireVector that is identical to w except in the range of bits specified. When multiple non-overlapping fields need to be updated in a single cycle this provides a clearer way to describe that behavior than iterative calls to bitfield_update() (although that is, in fact, what it is doing).

w = bitfield_update_set(w, {
        (20, 23):    0x6,      # sets bit 20 to 0, bits 21 and 22 to 1
        (26, None):  0x7,      # assuming w is 32 bits, sets bits 31..26 to 0x7
        (None, 1):   0x0,      # set the LSB (bit) to 0
    })
pyrtl.helperfuncs.match_bitpattern(w, bitpattern, field_map=None)[source]#

Returns a single-bit WireVector that is 1 if and only if w matches the bitpattern, and a tuple containing the matched fields, if any. Compatible with the with statement.

Parameters:
  • w (WireVector) – The WireVector to be compared to the bitpattern

  • bitpattern (str) – A string holding the pattern (of bits and wildcards) to match

  • field_map – (optional) A map from single-character field name in the bitpattern to the desired name of field in the returned namedtuple. If given, all non-“1”/”0”/”?” characters in the bitpattern must be present in the map.

Returns:

A tuple of 1-bit WireVector carrying the result of the comparison, followed by a named tuple containing the matched fields, if any.

This function will compare a multi-bit WireVector to a specified pattern of bits, where some of the pattern can be “wildcard” bits. If any of the 1 or 0 values specified in the bitpattern fail to match the WireVector during execution, a 0 will be produced, otherwise the value carried on the wire will be 1. The wildcard characters can be any other alphanumeric character, with characters other than ? having special functionality (see below). The string must have length equal to the WireVector specified, although whitespace and underscore characters will be ignored and can be used for pattern readability.

For all other characters besides 1, 0, or ?, a tuple of WireVectors will be returned as the second return value. Each character will be treated as the name of a field, and non-consecutive fields with the same name will be concatenated together, left-to-right, into a single field in the resultant tuple. For example, 01aa1?bbb11a will match a string such as 010010100111, and the resultant matched fields are:

(a, b) = (0b001, 0b100)

where the a field is the concenation of bits 9, 8, and 0, and the b field is the concenation of bits 5, 4, and 3. Thus, arbitrary characters beside ? act as wildcard characters for the purposes of matching, with the additional benefit of returning the WireVectors corresponding to those fields.

A prime example of this is for decoding instructions. Here we decode some RISC-V:

with pyrtl.conditional_assignment:
    with match_bitpattern(inst, "iiiiiiiiiiiirrrrr010ddddd0000011") as (imm, rs1, rd):
        regfile[rd] |= mem[(regfile[rs1] + imm.sign_extended(32)).truncate(32)]
        pc.next |= pc + 1
    with match_bitpattern(inst, "iiiiiiirrrrrsssss010iiiii0100011") as (imm, rs2, rs1):
        mem[(regfile[rs1] + imm.sign_extended(32)).truncate(32)] |= regfile[rs2]
        pc.next |= pc + 1
    with match_bitpattern(inst, "0000000rrrrrsssss111ddddd0110011") as (rs2, rs1, rd):
        regfile[rd] |= regfile[rs1] & regfile[rs2]
        pc.next |= pc + 1
    with match_bitpattern(inst, "0000000rrrrrsssss000ddddd0110011") as (rs2, rs1, rd):
        regfile[rd] |= (regfile[rs1] + regfile[rs2]).truncate(32)
        pc.next |= pc + 1
    # ...etc...

Some smaller examples:

m, _ = match_bitpattern(w, '0101')  # basically the same as w == '0b0101'
m, _ = match_bitpattern(w, '01?1')  # m will be true when w is '0101' or '0111'
m, _ = match_bitpattern(w, '??01')  # m be true when last two bits of w are '01'
m, _ = match_bitpattern(w, '??_0 1')  # spaces/underscores are ignored, same as line above
m, (a, b) = match_pattern(w, '01aa1?bbb11a')  # all bits with same letter make up same field
m, fs = match_pattern(w, '01aa1?bbb11a', {'a': 'foo', 'b': 'bar'})  # fields fs.foo, fs.bar

Creating Lists of WireVectors#

pyrtl.helperfuncs.input_list(names, bitwidth=None)[source]#

Allocate and return a list of Inputs.

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

  • bitwidth (int) – The desired bitwidth for the resulting Inputs.

Returns:

List of Inputs.

Return type:

List[Input]

Equivalent to:

wirevector_list(names, bitwidth, wvtype=pyrtl.wire.Input)
pyrtl.helperfuncs.output_list(names, bitwidth=None)[source]#

Allocate and return a list of Outputs.

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

  • bitwidth (int) – The desired bitwidth for the resulting Outputs.

Returns:

List of Outputs.

Return type:

List[Output]

Equivalent to:

wirevector_list(names, bitwidth, wvtype=pyrtl.wire.Output)
pyrtl.helperfuncs.register_list(names, bitwidth=None)[source]#

Allocate and return a list of Registers.

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

  • bitwidth (int) – The desired bitwidth for the resulting Registers.

Returns:

List of Registers.

Return type:

List[Register]

Equivalent to:

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

Allocate and return a list of WireVectors.

Parameters:
  • names – Names for the WireVectors. Can be a list or single comma/space-separated string

  • bitwidth (int) – The desired bitwidth for the resulting WireVectors.

  • wvtype (WireVector) – Which WireVector type to create.

Returns:

List of WireVectors.

Return type:

List[WireVector]

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])

Interpreting Vectors of Bits#

Under the hood, every single value a PyRTL design operates on is a bit vector (which is, in turn, simply an integer of bounded power-of-two size. Interpreting these bit vectors as humans, and turning human understandable values into their corresponding bit vectors, can both be a bit of a pain. The functions below do not create any hardware but rather help in the process of reasoning about bit vector representations of human understandable values.

pyrtl.helperfuncs.val_to_signed_integer(value, bitwidth)[source]#

Return value as intrepreted as a signed integer under two’s complement.

Parameters:
  • value (int) – a Python integer holding the value to convert

  • bitwidth (int) – the length of the integer in bits to assume for conversion

Returns:

value as a signed integer

Return type:

int

Given an unsigned integer (not a WireVector!) convert that to a signed integer. This is useful for printing and interpreting values which are negative numbers in two’s complement.

val_to_signed_integer(0xff, 8) == -1
pyrtl.helperfuncs.val_to_formatted_str(val, format, enum_set=None)[source]#

Return a string representation of the value given format specified.

Parameters:
  • val (int) – an unsigned integer to convert

  • format (str) – 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

Returns:

a human-readable string representing val.

Return type:

str

Given an unsigned integer (not a WireVector!) convert that to a human-readable string. 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.

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

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

Parameters:
  • data (str) – a string holding the value to convert

  • format (str) – 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 conversion process

Returns:

data as a signed integer

Return type:

int

Given a string (not a WireVector!) convert 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.infer_val_and_bitwidth(rawinput, bitwidth=None, signed=False)[source]#

Return a tuple (value, bitwidth) infered from the specified input.

Parameters:
  • rawinput – a bool, int, or verilog-style string constant

  • bitwidth (int) – an integer bitwidth or (by default) None

  • signed (bool) – a bool (by default set False) to include bits for proper two’s complement

Returns:

tuple of integers (value, bitwidth)

Return type:

(int, int)

Given a boolean, integer, or verilog-style string constant, this function returns a tuple of two integers (value, bitwidth) which are infered from the specified rawinput. The tuple returned is, in fact, a named tuple with names .value and .bitwidth for fields 0 and 1 respectively. If signed is True, bits will be included to ensure a proper two’s complement representation is possible, otherwise it is assume all bits can be used for standard unsigned representation. Error checks are performed that determine if the bitwidths specified are sufficient and appropriate for the values specified. Examples can be found below

infer_val_and_bitwidth(2, bitwidth=5) == (2, 5)
infer_val_and_bitwidth(3) == (3, 2)  # bitwidth infered from value
infer_val_and_bitwidth(3, signed=True) == (3, 3)  # need a bit for the leading zero
infer_val_and_bitwidth(-3, signed=True) == (5, 3)  # 5 = -3 & 0b111 = ..111101 & 0b111
infer_val_and_bitwidth(-4, signed=True) == (4, 3)  # 4 = -4 & 0b111 = ..111100 & 0b111
infer_val_and_bitwidth(-3, bitwidth=5, signed=True) == (29, 5)
infer_val_and_bitwidth(-3) ==> Error  # negative numbers require bitwidth or signed=True
infer_val_and_bitwidth(3, bitwidth=2) == (3, 2)
infer_val_and_bitwidth(3, bitwidth=2, signed=True) ==> Error  # need space for sign bit
infer_val_and_bitwidth(True) == (1, 1)
infer_val_and_bitwidth(False) == (0, 1)
infer_val_and_bitwidth("5'd12") == (12, 5)
infer_val_and_bitwidth("5'b10") == (2, 5)
infer_val_and_bitwidth("5'b10").bitwidth == 5
infer_val_and_bitwidth("5'b10").value == 2
infer_val_and_bitwidth("8'B 0110_1100") == (108, 8)
pyrtl.helperfuncs.log2(integer_val)[source]#

Return the log base 2 of the integer provided.

Parameters:

integer_val (int) – The integer to take the log base 2 of.

Returns:

The log base 2 of integer_val, or throw PyRTL error if not power of 2

Return type:

int

This function is useful when checking that powers of 2 are provided on inputs to functions. It throws an error if a negative value is provided or if the value provided is not an even power of two.

Examples:

log2(2)  # returns 1
log2(256)  # returns 8
addrwidth = log2(size_of_memory)  # will fail if size_of_memory is not a power of two

Debugging#

pyrtl.core.set_debug_mode(debug=True)[source]#

Set the global debug mode.

Parameters:

debug (bool) – Optional boolean paramter to which debug mode will be set

This function will set the debug mode to the specified value. Debug mode is, by default, set to off to keep the performance of the system. With debug mode set to true, all temporary WireVectors created will be given a name based on the line of code on which they were created and a snapshot of the call-stack for those WireVectors will be kept as well.

pyrtl.helperfuncs.probe(w, name=None)[source]#

Print useful information about a WireVector when in debug mode.

Parameters:
  • w (WireVector) – WireVector from which to get info

  • name (str) – optional name for probe (defaults to an autogenerated name)

Returns:

original WireVector w

Return type:

WireVector

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 an Output 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)[source]#

Add hardware assertions to be checked on the RTL design.

Parameters:
  • w (WireVector) – 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)

Return type:

Output

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)[source]#

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

Parameters:

sim (Simulation) – Simulation in which to check the assertions

Returns:

None

Reductions#

pyrtl.corecircuits.and_all_bits(vector)[source]#

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

Parameters:

vector (WireVector) – Takes a single arbitrary length WireVector

Returns:

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)[source]#

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

Parameters:

vector (WireVector) – Takes a single arbitrary length WireVector

Returns:

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)[source]#

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

Parameters:

vector (WireVector) – Takes a single arbitrary length WireVector

Returns:

Returns a 1 bit result, the bitwise xor of all of the bits in the vector to a single bit.

pyrtl.corecircuits.parity(vector)#

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

Parameters:

vector (WireVector) – Takes a single arbitrary length WireVector

Returns:

Returns a 1 bit result, the bitwise xor of all of the bits in the vector to a single bit.

pyrtl.corecircuits.rtl_any(*vectorlist)[source]#

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). If no inputs are provided it will return a Const 0 (since there are no ‘1’s present) similar to Python’s any function called with an empty list.

Examples:

rtl_any(thing1, thing2, thing3)  # same as thing1 | thing2 | thing3
rtl_any(*[list_of_things])  # the unpack operator ("*") can be used for lists
rtl_any()  # returns Const(False) which comes up if the list above is empty
pyrtl.corecircuits.rtl_all(*vectorlist)[source]#

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). If no inputs are provided it will return a Const 1 (since there are no ‘0’s present) similar to Python’s all function called with an empty list.

Examples:

rtl_all(thing1, thing2, thing3)  # same as thing1 & thing2 & thing3
rtl_all(*[list_of_things])  # the unpack operator ("*") can be used for lists
rtl_all()  # returns Const(True) which comes up if the list above is empty

Extended Logic and Arithmetic#

The functions below provide ways of comparing and arithmetically combining WireVectors in ways that are often useful in hardware design. The functions below extend those member functions of the WireVector class itself (which provides support for addition, unsigned multiplication, unsigned comparison, and many others).

pyrtl.corecircuits.signed_add(a, b)[source]#

Return a WireVector for result of signed addition.

Parameters:
  • a (WireVector) – a WireVector to serve as first input to addition

  • b (WireVector) – a WireVector to serve as second input to addition

Given WireVectors with length n and m, the result of the signed addition has length:

max(n, m) + 1

The inputs are two’s complement sign extended to the same length before adding. If an integer is passed to either a or b, it will be converted automatically to a two’s complement constant

pyrtl.corecircuits.signed_mult(a, b)[source]#

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

Parameters:
  • a (WireVector) – a wirevector to serve as first input to multiplication

  • b (WireVector) – a wirevector to serve as second input to multiplication

If an integer is passed to either a or b, it will be converted automatically to a two’s complement constant

pyrtl.corecircuits.signed_lt(a, b)[source]#

Return a single bit result of signed less than comparison.

pyrtl.corecircuits.signed_le(a, b)[source]#

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

pyrtl.corecircuits.signed_gt(a, b)[source]#

Return a single bit result of signed greater than comparison.

pyrtl.corecircuits.signed_ge(a, b)[source]#

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

pyrtl.corecircuits.shift_left_arithmetic(bits_to_shift, shift_amount)[source]#

Shift left arithmetic operation.

Parameters:
  • bits_to_shift (WireVector) – WireVector to shift left

  • shift_amount – WireVector or integer 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 they are identical. Note that shift_amount is treated as unsigned.

pyrtl.corecircuits.shift_right_arithmetic(bits_to_shift, shift_amount)[source]#

Shift right arithmetic operation.

Parameters:
  • bits_to_shift (WireVector) – WireVector to shift right

  • shift_amount – WireVector or integer 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)[source]#

Shift left logical operation.

Parameters:
  • bits_to_shift (WireVector) – WireVector to shift left

  • shift_amount – WireVector or integer 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. A logical shift is one that treats the value as as unsigned number, meaning the zeroes are shifted in. Note that shift_amount is treated as unsigned.

pyrtl.corecircuits.shift_right_logical(bits_to_shift, shift_amount)[source]#

Shift right logical operation.

Parameters:
  • bits_to_shift (WireVector) – WireVector to shift left

  • shift_amount – WireVector or integer 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. A 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.