Block and Logic Nets¶
Block and LogicNet are lower level PyRTL abstractions for
representing a hardware design. Most users won’t need to understand them,
unless they are implementing Analysis and Optimization passes or
modifying PyRTL itself.
GateGraphs are an alternative representation that makes it easier to write analysis passes.
Blocks¶
- class pyrtl.Block[source]¶
Blockencapsulates a netlist.A
Blockin PyRTL is the class that stores a netlist and provides basic access and error checking members. EachBlockhas well definedInputsandOutputs, and contains both the basic logic elements and references to theWireVectorsandMemBlocksthat connect them together.The logic structure is primarily contained in
logicwhich holds asetofLogicNets. EachLogicNetdescribes a primitive operation (such as an adder or memory), a 4-tuple of:The
op(a single character describing the operation such as+orr).A set of static
op_paramfor the operation, such as the bit slices to select for thes“selection” operation.A tuple
argscontaining theWireVectorsconnected as inputs to theLogicNet.A tuple
destscontaining theWireVectorsconnected as outputs from theLogicNet.
Below is a list of the basic operations. These properties (more formally specified) should all be checked by
sanity_check().Most logical and arithmetic
opsare pretty self explanatory. Each takes exactly twoargs, and they should perform the arithmetic or logical operation specified.ops:&,|,^,n,~,+,-,*.All inputs must be the same
bitwidth. Logical operations produce as many bits as are in the input, while+and-producen + 1bits, and*produces2 * nbits.In addition there are some operations for performing comparisons that should perform the operation specified. The
=opis checking to see if the bits of theargsvectors are equal, while<and>do unsigned arithmetic comparison. All comparisons generate a single bitdest(1forTrue,0forFalse).The
wopis simply a directional wire that connectsargstodests. It has no logic function.The
xopis a multiplexer which takes a select bit and two signals asargs. If the value of the select bit is0it selects the secondarg; if it is1it selects the thirdarg. Select must be a single bit, while the other twoargsmust be the same length.The
copis the concatenation operator and combines any number ofWireVectorargs(a,b, …,z) into a single newWireVectorwithain the MSB andz(or whatever is last) in the LSB position.The
sopis the selection operator and chooses, based on theop_paramspecified, a subset of the logic bits from aWireVectorto select. Repeats are accepted.The
ropis a register and on posedge, simply copies the value fromargto the register’sdest.The
mopis a memory block read port, which supports async reads (acting like combinational logic). Multiple read (and write) ports are possible to the same memory but eachmdefines only one of those. Theop_paramis a tuple containing two values: theMemBlock.id(memid), and a reference to theMemBlockcontaining this port. TheMemBlockshould only be used for debug and sanity checks. Each read port has oneaddr(anarg) and onedata(adest).The
@(update)opis a memory block write port, which supports synchronous writes (writes are “latched” at positive edge). Multiple write (and read) ports are possible to the same memory but each@defines only one of those. Theop_paramis a tuple containing two values: theMemBlock.id(memid), and a reference to theMemBlock. Writes have threeargs(addr,data, and write enablewe_en). Thedestsshould be an empty tuple. You will not see a written value change until the following cycle. If multiple writes happen to the same address in the same cycle the behavior is currently undefined.
The connecting elements (
argsanddests) should beWireVectorsor derived fromWireVector, and should be registered with theBlockusingadd_wirevector().LogicNetsshould be registered usingadd_net().In addition, there is a member
legal_opswhich defines the set of operations that can be legally added to theBlock. By default it is set to all of the above defined operations, but it can be useful in certain cases to only allow a subset of operations (such as when transforms are being done that are “lowering” theBlockto more primitive ops).See
LogicNetfor a complete list of defined operations.- add_net(net)[source]¶
Add logic to the
Block.The passed
LogicNetis checked and added to theBlock. NoWireVectorsare added; they must be added seperately withadd_wirevector().
- add_wirevector(wirevector)[source]¶
- Parameters:
wirevector (
WireVector) –WireVectorto add toself.
- get_memblock_by_name(name, strict=False)[source]¶
Get a
MemBlockfrom theBlock, by name.Useful for getting access to internal
MemBlocksfor testing. For example, theSimulationconstructor requires aMemBlockreference for itsmemory_value_map. Instead of passing theMemBlockaround in your code, you can get a reference to theMemBlockfrom the globalworking_block():>>> def read_hidden_mem(read_addr: pyrtl.WireVector) -> pyrtl.WireVector: ... mem = pyrtl.MemBlock(bitwidth=8, addrwidth=4, name='hidden_mem') ... return mem[read_addr] >>> read_addr = pyrtl.Input(4, 'read_addr') >>> data = pyrtl.Output(8, 'data') >>> data <<= read_hidden_mem(read_addr) >>> hidden_mem = pyrtl.working_block().get_memblock_by_name('hidden_mem') >>> sim = pyrtl.Simulation(memory_value_map={hidden_mem: {3: 7}}) >>> sim.step(provided_inputs={read_addr.name: 3}) >>> sim.inspect(data.name) 7
Returns
Noneif no matchingMemBlockcan be found. However, ifstrict=True, then this will instead throw aPyrtlErrorwhen no match is found.
- get_wirevector_by_name(name, strict=False)[source]¶
Return the
WireVectorwith matchingname.- Parameters:
name (
str) – Name ofWireVector.strict (
bool, default:False) – IfTrue, raise an exception when no matchingWireVectoris found. Defaults to False.
- Raises:
PyrtlError – if
strict=Trueand no matchingWireVectoris found.- Return type:
- Returns:
The
WireVectorwith the specifiedname.
- logic_subset(op=None)[source]¶
Return a subset of the
Block'sLogicNets.Filters
LogicNetsby theirop.If
opisNone, returns all theLogicNetsassociated with theBlock. Otherwise, returns a set of theLogicNetswith one of the specifiedops. This is helpful for getting all memories of a block for example.
- net_connections(include_virtual_nodes=False)[source]¶
Returns sources and sinks for each
WireVectorin theBlock.A
WireVector’s source is theLogicNetthat sets theWireVector’s value.A
WireVector’s destinations are theLogicNetsthat use theWireVector’s value.This information helps when building a graph representation for the
Block. Seenet_graph()for an example.Note
Consider using GateGraphs instead.
- Parameters:
include_virtual_nodes (
bool, default:False) – IfTrue, external sources (such as anInputsandConsts) will be represented as wires that set themselves, and external destinations (such asOutputs) will be represented as wires that use themselves. IfFalse, these nodes will be excluded from the results.- Return type:
tuple[dict[WireVector,LogicNet],dict[WireVector,list[LogicNet]]]- Returns:
Two dictionaries. The first maps
WireVectorsto theLogicNetthat creates their signal (wire_src_dict). The second mapsWireVectorsto a list ofLogicNetsthat use their signal (wire_dst_dict).
- remove_wirevector(wirevector)[source]¶
- Parameters:
wirevector (
WireVector) –WireVectorto remove fromself.
- sanity_check()[source]¶
Verify the
Block’s integrity. Raise an exception if there is an issue.sanity_checkdoes not change any of theBlock’s state. It only verifies that theBlock’s data structures are internally consistent.- Raises:
PyrtlError – If the
Blockis malformed.
- wirevector_subset(cls=None, exclude=())[source]¶
Return a subset of the
Block'sWireVectors.Filters
WireVectorsby type.If
clsisNone, returns all theWireVectorsassociated with theBlock. Ifclsis a single type, or a tuple of types, onlyWireVectorsof the matching types will be returned. This is helpful for getting all of aBlock’sInputs,Outputs, orRegistersfor example:>>> len([pyrtl.Input(bitwidth=4) for _ in range(2)]) 2 >>> len([pyrtl.Output(bitwidth=4) for _ in range(3)]) 3 >>> block = pyrtl.working_block() >>> inputs = block.wirevector_subset(pyrtl.Input) >>> len(inputs) 2 >>> all(isinstance(input, pyrtl.Input) for input in inputs) True >>> non_inputs = block.wirevector_subset(exclude=pyrtl.Input) >>> len(non_inputs) 3 >>> any(isinstance(non_input, pyrtl.Input) for non_input in non_inputs) False
- Parameters:
cls (
tuple[type] |None, default:None) – Type ofWireVectorobjects to include in the returnedset.exclude (
tuple[type], default:()) – Type ofWireVectorobjects to exclude from the returnedset.
- Return type:
- Returns:
WireVectorsin theBlockthat are both aclstype and not anexcludedtype.
working_block¶
Most PyRTL operations operate on the global working_block by default. PyRTL
provides several functions to inspect and manipulate the working_block:
- pyrtl.working_block(block=None)[source]¶
Convenience function for capturing the current working block.
If a
blockis not passed, or if theblockpassed isNone, then this will return the “current working block”. However, if ablockis passed in it will simply returnblockinstead. This feature is useful in allowing functions to “override” the current working block.- Return type:
LogicNets¶
- class pyrtl.LogicNet(op: str, op_param: tuple, args: tuple[WireVector], dests: tuple[WireVector])[source]¶
The basic immutable datatype for storing a “net” in a netlist.
LogicNetis part of PyRTL’s internal representation. What it is, and how it is used are only required for advanced PyRTL users.A “net” is a Python representation of a hardware logic operation. These operations include binary operations like and or and not, arithmetic operations like + and -, and other operations like concatenating wires, splitting wires, reading/writing memory, and register logic:
op
op_param
args
dests
&Nonea1, a2outAND two wires together, put result into
out|Nonea1, a2outOR two wires together, put result into
out^Nonea1, a2outXOR two wires together, put result into
outnNonea1, a2outNAND two wires together, put result into
out~Nonea1outInvert one wire, put result into
out.+Nonea1, a2outAdd
a1anda2, put result intoout.len(out) == max(len(a1), len(a2)) + 1Performs unsigned addition. Use
signed_add()for signed addition.-Nonea1, a2outSubtract
a2froma1, put result intoout.len(out) == max(len(a1), len(a2)) + 1Performs unsigned subtraction. Use
signed_sub()for signed subtraction.*Nonea1, a2outMultiply
a1anda2, put result intoout.len(out) == len(a1) + len(a2)Performs unsigned multiplication. Use
signed_mult()for signed multiplication.=Nonea1, a2outCheck if
a1anda2are equal, put result intoout.outhas bitwidth 1.<Nonea1, a2outCheck if
a1is less thana2, put result intoout.outhas bitwidth 1.Performs unsigned comparison. Use
signed_lt()for signed less than.>Nonea1, a2outCheck if
a1is greater thana2, put result intooutouthas bitwidth 1.Performs unsigned comparison. Use
signed_gt()for signed greater than.wNonew1w2Connects
w1tow2. This is a directional wire with no logical function.xNonex,a1, a2outMultiplexer:
When
x == 0, connecta1toout.When
x == 1, connecta2toout.xmust be 1-bit andlen(a1) == len(a2).cNone*argsoutConcatenates
argsinto a singleWireVector.The first
argbecomes the most significant bits, and the lastargbecomes the least significant bits.sselwireoutSelects bits from
wirebased onsel(slicing syntax).Puts the selected bits into
out.rNonenextr1On positive clock edge: copy
nexttor1.mmemid,memaddrdataRead address
addrofMemBlockmem(withidmemid), put the data read intodata.@memid,memaddrdata,wr_enWrite
datatoMemBlockmem(withidmemid) at addressaddr, ifwr_enis1. This is the onlyopwith nodests.-
args:
tuple[WireVector]¶ Input arguments to the operation.
-
dests:
tuple[WireVector]¶ Output of the operation.
-
args:
GateGraphs¶
GateGraph is an alternative representation for PyRTL logic.
Motivation¶
PyRTL represents logic internally with WireVectors and
LogicNets. For example, the following code creates five
WireVectors and two LogicNets:
>>> a = pyrtl.Input(name="a", bitwidth=1)
>>> b = pyrtl.Input(name="b", bitwidth=1)
>>> c = pyrtl.Input(name="c", bitwidth=1)
>>> x = a & b
>>> x.name = "x"
>>> y = x | c
>>> y.name = "y"
>>> print(pyrtl.working_block())
x/1W <-- & -- a/1I, b/1I
y/1W <-- | -- x/1W, c/1I
The WireVectors and LogicNets are arranged
like this:
┌──────────────┐
│ LogicNet "&" │
│ op: "&" │ ┌────────────────┐
│ args:────┼───▶│ WireVector "a" │
│ args:────┼─┐ └────────────────┘
│ │ │ ┌────────────────┐
│ │ └─▶│ WireVector "b" │
│ │ └────────────────┘
│ │ ┌────────────────┐
│ dests:───┼───▶│ WireVector "x" │
└──────────────┘ ┌─▶└────────────────┘
┌──────────────┐ │
│ LogicNet "|" │ │
│ op: "|" │ │
│ args:────┼─┘ ┌────────────────┐
│ args:────┼───▶│ WireVector "c" │
│ │ └────────────────┘
│ │ ┌────────────────┐
│ dests:───┼───▶│ WireVector "y" │
└──────────────┘ └────────────────┘
This data structure is difficult to work with for three reasons:
The arrows do not consistently point from producer to consumer, or from consumer to producer. For example, there is no arrow from
WireVectorx(producer) toLogicNet|(consumer). Similarly, there is no arrow fromWireVectorx(consumer) toLogicNet&(producer). These missing arrows make it impossible to iteratively traverse the data structure. This creates a need for methods likenet_connections(), which createswire_src_dictandwire_sink_dictwith the missing pointers.The data structure is composed of two different classes,
LogicNetandWireVector, and these two classes have completely different interfaces. As we follow pointers from one class to another, we must keep track of the current object’s class, and interact with it appropriately.WireVectoris part of PyRTL’s user interface, but also a key part of PyRTL’s internal representation. This makesWireVectorcomplex and difficult to modify, because it must implement user-facing features like inferring bitwidth from assignment, while also maintaining a consistent internal representation for simulation, analysis, and optimization.
GateGraph is an alternative representation that addresses these issues. A
GateGraph is just a collection of Gates, so we’ll cover
Gate first.
- class pyrtl.Gate(logic_net=None, wire_vector=None, args=None)[source]¶
Gateis an alternative to PyRTL’s defaultLogicNetandWireVectorrepresentation.Gatemakes it easy to iteratively explore a circuit, while simplifying the circuit’s representation by making everything aGate. AGateis equivalent to aLogicNetfused with itsdestWireVector. So thisLogicNetandWireVector:┌──────────────────┐ │ LogicNet │ ┌───────────────────┐ │ op: o │ │ WireVector │ │ args: [x, y] │ │ name: n │ │ dests:───────┼───▶│ bitwidth: b │ └──────────────────┘ └───────────────────┘
Are equivalent to this
Gate:┌─────────────────────┐ │ Gate │ │ op: o │ │ args: [x, y] │ │ name: n │ │ bitwidth: b │ │ dests: [g1, g2] │ └─────────────────────┘
Key differences between the two representations:
The
WireVector’snameandbitwidthare stored asGate.nameandGate.bitwidth. If theLogicNetproduces no output, like aMemBlockwrite, theGate’snameandbitwidthwill beNone. PyRTL does not have anopwith multipleLogicNet.dests.The
Gatehas a newGate.destsattribute, which has no direct equivalent in theLogicNet/WireVectorrepresentation.Gate.destsis a list of theGatesthat use thisGate’s output as one of theirargs.
LogicNet.destsandGate.destsrepresent slightly different things, despite having similar names:LogicNet.destsrepresents theLogicNet’s output wire. It is a list ofWireVectorswhich hold theLogicNet’s output. There can only be zero or oneWireVectorsinLogicNet.dests, but thatWireVectorcan be anargto any number ofLogicNets.Gate.destsrepresents theGate’s users. It is a list ofGatesthat use theGate’s output as one of theirargs. There can be any number ofGatesinGate.dests.
With
Gates, the example from the Motivation section looks like:┌─────────────────┐ │ Gate "a" │ │ op: "I" │ │ name: "a" │ │ bitwidth: 1 │ ┌─────────────────┐ │ dests:──────┼───▶│ Gate "&" │ └─────────────────┘◀─┐ │ op: "&" │ ┌─────────────────┐ └─┼─────args │ │ Gate "b" │◀───┼─────args │ │ op: "I" │ │ name: "x" │ ┌─────────────────┐ │ name: "b" │ │ bitwidth: 1 │ │ Gate "|" │ │ bitwidth: 1 │ ┌─▶│ dests:──────┼───▶│ op: "|" │ │ dests:──────┼─┘ └─────────────────┘◀───┼─────args │ └─────────────────┘ ┌────────────────────────┼─────args │ ┌─────────────────┐ │ │ name: "y" │ │ Gate "c" │◀─┘ ┌───────────────────▶│ bitwidth: 1 │ │ op: "I" │ │ └─────────────────┘ │ name: "c" │ │ │ bitwidth: 1 │ │ │ dests:──────┼──────┘ └─────────────────┘
With a
Gaterepresentation, it is easy to iteratively traverse the data structure:Forwards, from producer to consumer, by following
destsreferences.Backwards, from consumer to producer, by following
argsreferences.
The
Gaterepresentation addresses the issues raised in the Motivation section:The
Gaterepresentation is easy to iteratively explore by followingargsanddestsreferences, which are shown as arrows in the figure above.There is only one class in the
Gategraph, so we don’t need to keep track of the current object’s type as we follow arrows in the graph, like we did withLogicNetandWireVector. Everything is aGate.By decoupling the
Gaterepresentation fromWireVectorandLogicNet,Gatespecializes in supporting analysis use cases, without the burden of supporting all ofWireVector’s other features. This significantly simplifiesGate’s design and implementation.
For usage examples, see
GateGraphandGate’s documentation below.- __str__()[source]¶
- Return type:
- Returns:
A string representation of this
Gate.
Example:
>>> a = pyrtl.Input(name="a", bitwidth=8) >>> bit_slice = a[2:4] >>> bit_slice.name = "bit_slice" >>> gate_graph = pyrtl.GateGraph() >>> bit_slice_gate = gate_graph.get_gate("bit_slice") >>> print(bit_slice_gate) bit_slice/2 = slice(a/8) [sel=(2, 3)]
In this sample string representation:
nameisbit_slice.bitwidthis2.opiss, spelled out assliceto improve readability.argsis[<Gate for "a">]:>>> bit_slice_gate.args[0] is gate_graph.get_gate("a") True
op_paramis(2, 3), written asselbecause aslice’sop_paramdetermines the selected bits. This improves readability by indicating what theop_parammeans for theop.
-
args:
list[Gate]¶ Inputs to the operation. Corresponds to
LogicNet.args.For each
Gatearginself.args,selfis inarg.dests.Some special
Gatesrepresent operations withoutargs, likeInputandConst. Such operations will have an empty list ofargs.Note
The same
Gatemay appear multiple times inargs. A self-loopRegisterGatemay be its ownarg.Example:
>>> a = pyrtl.Input(name="a", bitwidth=1) >>> b = pyrtl.Input(name="b", bitwidth=1) >>> c = pyrtl.Input(name="c", bitwidth=1) >>> abc = pyrtl.concat(a, b, c) >>> abc.name = "abc" >>> gate_graph = pyrtl.GateGraph() >>> abc_gate = gate_graph.get_gate("abc") >>> [gate.name for gate in abc_gate.args] ['a', 'b', 'c']
-
bitwidth:
int|None¶ Bitwidth of the operation’s output. Corresponds to
WireVector.bitwidth.Some operations do not have outputs, like
MemBlockwrites. These operations will have abitwidthofNone.Example:
>>> a = pyrtl.Input(name="a", bitwidth=1) >>> b = pyrtl.Input(name="b", bitwidth=1) >>> ab = a + b >>> ab.name = "ab" >>> gate_graph = pyrtl.GateGraph() >>> ab_gate = gate_graph.get_gate("ab") >>> ab_gate.bitwidth 2
- property const_value: int¶
Readability alias for
op_param, returning the value for aConst(C)op.Example:
>>> const = pyrtl.Const(name="const", val=33) >>> gate_graph = pyrtl.GateGraph() >>> const_gate = gate_graph.get_gate("const") >>> const_gate.const_value 33
-
dests:
list[Gate]¶ listofGatesthat use this operation’s output as one of theirargs.For each
Gatedestinself.dests,selfis indest.args.Note
The same
Gatemay appear multiple times indests. A self-loopRegisterGatemay appear in its owndests.Example:
>>> a = pyrtl.Input(name="a", bitwidth=1) >>> _ = a + 1 >>> _ = a - 1 >>> gate_graph = pyrtl.GateGraph() >>> a_gate = gate_graph.get_gate("a") >>> [gate.op for gate in a_gate.dests] ['+', '-']
-
is_output:
bool¶ Indicates if the operation’s output is an
Output.Example:
>>> a = pyrtl.Input(name="a", bitwidth=1) >>> b = pyrtl.Output(name="b", bitwidth=1) >>> b <<= a >>> gate_graph = pyrtl.GateGraph() >>> a_gate = gate_graph.get_gate("a") >>> a_gate.is_output False >>> b_gate = gate_graph.get_gate("b") >>> b_gate.is_output True
- property mem: int¶
Readability alias for
op_param, returning theMemBlockfor a read (m) or write (@)op.Example:
>>> addr = pyrtl.Input(name="addr", bitwidth=4) >>> mem = pyrtl.MemBlock(addrwidth=4, bitwidth=8) >>> read = mem[addr] >>> read.name = "read" >>> gate_graph = pyrtl.GateGraph() >>> read_gate = gate_graph.get_gate("read") >>> read_gate.mem is mem True
- property memid: int¶
Readability alias for
op_param, returning theMemBlock.idfor a read (m) or write (@)op.Example:
>>> addr = pyrtl.Input(name="addr", bitwidth=4) >>> mem = pyrtl.MemBlock(addrwidth=4, bitwidth=8) >>> read = mem[addr] >>> read.name = "read" >>> gate_graph = pyrtl.GateGraph() >>> read_gate = gate_graph.get_gate("read") >>> read_gate.memid 0 >>> mem.id 0
-
name:
str|None¶ Name of the operation’s output. Corresponds to
WireVector.name.Some operations do not have outputs, like
MemBlockwrites. These operations will have anameofNone.Example:
>>> a = pyrtl.Input(name="a", bitwidth=1) >>> b = pyrtl.Input(name="b", bitwidth=1) >>> ab = a + b >>> ab.name = "ab" >>> gate_graph = pyrtl.GateGraph() >>> ab_gate = gate_graph.get_gate("ab") >>> ab_gate.name 'ab'
-
op:
str¶ Operation performed by this
Gate. Corresponds toLogicNet.op.For special
Gatescreated forInputs,opwill instead be theInput’s_code, which isI.For special
Gatescreated forConsts,opwill instead be theConst’s_code, which isC.See
LogicNet’s documentation for a description of all otherops.Example:
>>> a = pyrtl.Input(name="a", bitwidth=1) >>> _ = ~a >>> gate_graph = pyrtl.GateGraph() >>> gate_a = gate_graph.get_gate("a") >>> gate_a.op 'I' >>> gate_a.dests[0].op '~'
-
op_param:
tuple¶ Static parameters for the operation. Corresponds to
LogicNet.op_param.These are constant parameters, whose values are statically known. These values generally do not appear as actual values on wires. For example, the bits to select for the
sbit-slice operation are stored asop_params.Note
Consider using the aliases
const_value,reset_value,sel,memid,meminstead, which improve code readability.Example:
>>> a = pyrtl.Input(name="a", bitwidth=8) >>> bit_slice = a[1:3] >>> bit_slice.name = "bit_slice" >>> gate_graph = pyrtl.GateGraph() >>> bit_slice_gate = gate_graph.get_gate("bit_slice") >>> bit_slice_gate.op_param (1, 2)
- property reset_value: int¶
Readability alias for
op_param, returning the reset value for aRegister(r)op.Example:
>>> counter = pyrtl.Register(name="counter", bitwidth=8, reset_value=42) >>> counter.next <<= counter + 1 >>> gate_graph = pyrtl.GateGraph() >>> counter_gate = gate_graph.get_gate("counter") >>> counter_gate.reset_value 42
- property sel: tuple[int]¶
Readability alias for
op_param, returning the bits selected by a bit-slice (s)op.Example:
>>> a = pyrtl.Input(name="a", bitwidth=8) >>> bit_slice = a[2:6] >>> bit_slice.name = "bit_slice" >>> gate_graph = pyrtl.GateGraph() >>> bit_slice_gate = gate_graph.get_gate("bit_slice") >>> bit_slice_gate.sel (2, 3, 4, 5)
- class pyrtl.GateGraph(block=None)[source]¶
A
GateGraphis a collection ofGates.GateGraph’s constructor createsGatesfrom aBlock.See Motivation for more background.
Users should generally construct
GateGraphs, rather than attempting to directly construct individualGates.Gateconstruction is complex because they are doubly-linked, and theGategraph may contain cycles.Example¶
Let’s build a
GateGraphfor the Motivation example:>>> a = pyrtl.Input(name="a", bitwidth=1) >>> b = pyrtl.Input(name="b", bitwidth=1) >>> c = pyrtl.Input(name="c", bitwidth=1) >>> x = a & b >>> x.name = "x" >>> y = x | c >>> y.name = "y" >>> gate_graph = pyrtl.GateGraph()
The
GateGraphcan be printed, revealing fiveGates:>>> print(gate_graph) a/1 = Input b/1 = Input c/1 = Input x/1 = and(a/1, b/1) y/1 = or(x/1, c/1)
We can retrieve the
Gatefor inputa:>>> a = gate_graph.get_gate("a") >>> print(a) a/1 = Input >>> a.name 'a' >>> a.op 'I'
We can check
a’sdeststo see that it is an argument to a bitwise&operation, with output namedx:>>> len(a.dests) 1 >>> x = a.dests[0] >>> print(x) x/1 = and(a/1, b/1) >>> x.op '&' >>> x.name 'x'
We can examine the bitwise
&’sargs, to get references to inputGatesaandb:>>> x.args[0] is a True >>> b = x.args[1] >>> print(b) b/1 = Input >>> b.name 'b' >>> b.op 'I'
Special
Gates¶Generally,
GateGraphconverts eachLogicNetin aBlockto a correspondingGate, but someWireVectorsandLogicNetsare handled differently:An
InputWireVectoris converted to a special inputGate, with opI. InputGateshave noargs, and do not correspond to aLogicNet.A
ConstWireVectoris converted to a special constGate, with opC. ConstGateshave noargs, and do not correspond to aLogicNet. The constant’s value is stored inGate.op_param.An
OutputWireVectoris handled normally, and will be thedestof theGatethat defines theOutput’s value. ThatGatewill have itsis_outputattribute set toTrue.RegisterWireVectorsandLogicNetsare handled normally, except that theRegister’sreset_valueis stored inGate.op_param. RegisterGatesuse the registerLogicNetopr, not theRegister_codeR.
Note
Registers can create cycles in the
Gategraph, because the logic that defines the register’snextvalue (which is the registerGate’sargs) can depend on the register’s current value (which is the registerGate’sdests). Watch out for infinite loops when traversing aGateGraphwith registers. For example, if you keep followingdestsreferences, you may end up back where you started.- __init__(block=None)[source]¶
-
Most users should call this constructor, rather than attempting to directly construct individual
Gates.- Parameters:
block (
Block, default:None) –Blockto construct theGateGraphfrom. Defaults to the working_block.
- __iter__()[source]¶
Iterate over each gate in the
GateGraph.Similar to
gates, except that__iter__returns anIterablerather than aset.Example:
>>> a = pyrtl.Input(name="a", bitwidth=2) >>> b = pyrtl.Input(name="b", bitwidth=2) >>> sum = a + b >>> sum.name = "sum" >>> gate_graph = pyrtl.GateGraph() >>> sorted(gate.name for gate in gate_graph) ['a', 'b', 'sum']
- __str__()[source]¶
Return a string representation of the
GateGraph.Example:
>>> a = pyrtl.Input(name="a", bitwidth=2) >>> b = pyrtl.Input(name="b", bitwidth=2) >>> sum = a + b >>> sum.name = "sum" >>> gate_graph = pyrtl.GateGraph() >>> print(gate_graph) a/2 = Input b/2 = Input sum/3 = add(a/2, b/2)
-
consts:
set[Gate]¶ A
setofConstGatesin theGateGraph.These
Gatesprovide constant values, withopC.Example:
>>> c = pyrtl.Const(name="c", val=0) >>> d = pyrtl.Const(name="d", val=1) >>> _ = c + d >>> gate_graph = pyrtl.GateGraph() >>> sorted(gate.name for gate in gate_graph.consts) ['c', 'd']
-
gates:
set[Gate]¶ A
setof allGatesin theGateGraph.Similar to
__iter__, except thatgatesis asetrather than anIterable.Example:
>>> a = pyrtl.Input(name="a", bitwidth=1) >>> b = pyrtl.Input(name="b", bitwidth=1) >>> c = pyrtl.Input(name="c", bitwidth=1) >>> x = a & b >>> x.name = "x" >>> y = x | c >>> y.name = "y" >>> gate_graph = pyrtl.GateGraph() >>> sorted(gate.name for gate in gate_graph.gates) ['a', 'b', 'c', 'x', 'y']
- get_gate(name)[source]¶
Return the
Gatewhosenameisname, orNoneif no suchGateexists.Warning
MemBlockwrites do not produce an output, so they can not be retrieved withget_gate.Example:
>>> a = pyrtl.Input(name="a", bitwidth=1) >>> na = ~a >>> na.name = "na" >>> gate_graph = pyrtl.GateGraph() >>> a_gate = gate_graph.get_gate("a") >>> na_gate = gate_graph.get_gate("na") >>> na_gate.op '~' >>> na_gate.args[0] is a_gate True
-
inputs:
set[Gate]¶ A
setofInputGatesin theGateGraph.These
GatesprovideInputvalues, withopI.Example:
>>> a = pyrtl.Input(name="a", bitwidth=1) >>> b = pyrtl.Input(name="b", bitwidth=1) >>> _ = a & b >>> gate_graph = pyrtl.GateGraph() >>> sorted(gate.name for gate in gate_graph.inputs) ['a', 'b']
-
mem_reads:
set[Gate]¶ A
setofMemBlockreadGatesin theGateGraph.These
GatesreadMemBlocks, withopm.Example:
>>> mem = pyrtl.MemBlock(name="mem", bitwidth=4, addrwidth=2) >>> addr = pyrtl.Input(name="addr", bitwidth=2) >>> mem_read_1 = mem[addr] >>> mem_read_1.name = "mem_read_1" >>> mem_read_2 = mem[addr] >>> mem_read_2.name = "mem_read_2" >>> gate_graph = pyrtl.GateGraph() >>> sorted(gate.name for gate in gate_graph.reads) ['mem_read_1', 'mem_read_2']
-
mem_writes:
set[Gate]¶ A
setofMemBlockwriteGatesin theGateGraph.These
GateswriteMemBlocks, withop@.Example:
>>> mem = pyrtl.MemBlock(name="mem", bitwidth=4, addrwidth=2) >>> addr = pyrtl.Input(name="addr", bitwidth=2) >>> mem[addr] <<= 7 >>> gate_graph = pyrtl.GateGraph() >>> # MemBlock writes have no name. >>> [gate.name for gate in gate_graph.mem_writes] [None] >>> [gate.op for gate in gate_graph.mem_writes] ['@']
-
outputs:
set[Gate]¶ A
setofOutputGatesin theGateGraph.These
GatessetOutputvalues, withis_outputTrue.Example:
>>> x = pyrtl.Output(name="x") >>> y = pyrtl.Output(name="y") >>> x <<= 42 >>> y <<= 255 >>> gate_graph = pyrtl.GateGraph() >>> sorted(gate.name for gate in gate_graph.outputs) ['x', 'y']
-
registers:
set[Gate]¶ A
setofRegisterupdateGatesin theGateGraph.These
Gatesset aRegister’s value for the next cycle, withopr.Example:
>>> r = pyrtl.Register(name="r", bitwidth=1) >>> s = pyrtl.Register(name="s", bitwidth=1) >>> r.next <<= r + 1 >>> s.next <<= s + 2 >>> gate_graph = pyrtl.GateGraph() >>> sorted(gate.name for gate in gate_graph.registers) ['r', 's']
-
sinks:
set[Gate]¶ A
setofsinkGatesin theGateGraph.A
sinkGate’s output value is known only at the end of each clock cycle.Registers,Outputs,MemBlockwrites, and anyGatewithout users (len(dests) == 0) are sinkGates.Note
Registersare bothsourcesandsinks. As asource, it provides theRegister’s value for the current cycle. As asink, it determines theRegister’s value for the next cycle.Example:
>>> a = pyrtl.Input(name="a", bitwidth=1) >>> r = pyrtl.Register(name="r", bitwidth=1) >>> o = pyrtl.Output(name="o", bitwidth=1) >>> r.next <<= a + 1 >>> o <<= 1 >>> sum = a + r >>> sum.name = "sum" >>> gate_graph = pyrtl.GateGraph() >>> sorted(gate.name for gate in gate_graph.sinks) ['o', 'r', 'sum']
-
sources:
set[Gate]¶ A
setofsourceGatesin theGateGraph.A
sourceGate’s output value is known at the beginning of each clock cycle.Consts,Inputs, andRegistersaresourceGates.Note
Registersare bothsourcesandsinks. As asource, it provides theRegister’s value for the current cycle. As asink, it determines theRegister’s value for the next cycle.Example:
>>> a = pyrtl.Input(name="a", bitwidth=1) >>> c = pyrtl.Const(name="c", bitwidth=1, val=0) >>> r = pyrtl.Register(name="r", bitwidth=1) >>> r.next <<= a + c >>> gate_graph = pyrtl.GateGraph() >>> sorted(gate.name for gate in gate_graph.sources) ['a', 'c', 'r']