Skip to content
julianspeith edited this page Aug 12, 2026 · 20 revisions

A (logical) gate is an electronic component comprising multiple in- and outputs that implements one or more Boolean function(s). A gate may implement either combinational or sequential logic and is commonly connected to a number of nets via its input and output pins. It can thus be used in larger circuits to implement complex logic operations. Such a circuit is usually referred to as a gate-level netlist.

Every gate is an instance of a gate type taken from the netlist's gate library. The gate type is what gives a gate meaning — it defines the pins the gate has and the Boolean functions it computes. The gate itself only adds identity (an ID and a name), connectivity (which nets are attached to which pins), and instance-specific data such as a LUT's configuration string.

Gates are the nodes of the netlist graph, and most analyses ultimately come down to asking two questions about them: what does this gate compute? and what is it connected to?

Gate information and management

Similar to all other netlist components in HAL, a gate comprises basic information such as an ID, a name, and a gate type that can be read and sometimes written using dedicated commands such as get_id, get_name, set_name, and get_type. A gate can be created by using create_gate on the netlist while specifying its gate type and name. Furthermore, a gate can return the module and the grouping it is assigned to using get_module and get_grouping, and the netlist it belongs to using get_netlist.

g = netlist.create_gate(some_gt, "example_gate")   # create a new gate
id = g.get_id()                                    # get the gate's ID
name = g.get_name()                                # get the gate's name
gt = g.get_type()                                  # get the gate's gate type
grouping = g.get_grouping()                        # get the gate's grouping

Note that names are not unique in HAL and, in a netlist recovered from hardware, usually not meaningful either. Use IDs when you need to refer to a gate unambiguously.

Every gate is assigned to exactly one module. get_module returns that module, while get_modules returns the whole chain of modules containing the gate up to the top module, which is convenient for figuring out where in the hierarchy a gate sits.

m = g.get_module()      # the module the gate is directly assigned to
chain = g.get_modules() # all modules containing the gate, including the top module

Additionally, a gate can be retrieved from the netlist by ID using get_gate_by_id and may be deleted using delete_gate. Note that deleting a gate may result in dangling wires.

g = netlist.get_gate_by_id(3)   # get the gate with ID 3 from the netlist
netlist.delete_gate(g)          # delete the gate

Gate connections

Each gate has multiple input, output, inout or internal pins that are potentially connected to a variety of nets. The user can retrieve the pins via its gate type. The net connected to a pin can be retrieved using get_fan_in_net and get_fan_out_net by providing either the pin itself or just its name. If required, the endpoint of a pin is accessible via get_fan_in_endpoint and get_fan_out_endpoint. A list of all connected nets or endpoints of the gate is also available via get_fan_in_nets, get_fan_out_nets, get_fan_in_endpoints and get_fan_out_endpoints. To check whether a specific net is connected to the gate, use is_fan_in_net and is_fan_out_net.

out_net = g.get_fan_out_net("O")               # get net connected to output pin "O"
out_ep = g.get_fan_out_endpoint("O")           # get endpoint of output pin "O"
in_eps = g.get_fan_in_endpoints()              # get all endpoints at input pins

Prefer the endpoint variants whenever the pin matters and not just the net. Knowing that a net arrives at a flip-flop tells you little; knowing that it arrives at its clock pin rather than its data pin tells you a lot.

Easy access to the predecessors and successors of a gate is provided by the functions get_predecessors and get_successors. Both functions return a list of endpoints. Each of the endpoints describes the successor/predecessor gate, the destination/source pin (i.e., the pin at the successor/predecessor gate), and the net through which the successor/predecessor is connected to the current gate. The functions get_unique_predecessors and get_unique_successors return a list of gates with each gate being included in the list at most once, no matter whether the gate is predecessor or successor to multiple pins or just a single one.

pred = g.get_predecessors()               # get all predecessor endpoints
unique_succ = g.get_unique_successors()   # get all unique successor gates

If you are interested in the gate connected to one specific pin, get_predecessor and get_successor take a pin (or pin name) and return a single endpoint, or None if nothing is connected.

d_pred = g.get_predecessor("D")   # endpoint driving the "D" pin of a flip-flop

Note that these functions traverse exactly one gate at a time. To follow a signal through a chain of combinational logic to the next flip-flop — which is usually what you actually want — use the traversal helpers described in Decorators and Netlist Utilities instead of writing the loop yourself.

Boolean functions

The function(s) implemented by a gate are defined by their Boolean function(s). Usually, these Boolean functions are specified by the gate type and not the gate itself. However, for FPGA netlists containing LUTs it is not sufficient to store the Boolean function solely with the gate type since different gates of the same LUT gate type may implement different functions. Therefore, the functions get_boolean_function and get_boolean_functions may be used to retrieve Boolean functions associated with a gate and its gate type. Hence we recommend always retrieving Boolean functions from individual gates and not gate types. For the LUT gate type, Boolean functions are automatically created from their initialization string and cached until this string changes. Additional Boolean functions may be added to a gate using add_boolean_function.

func = gate.get_boolean_function("O")      # get Boolean function with name "O"
all_funcs = gate.get_boolean_functions()   # get all Boolean functions associated with gate and its type

Calling get_boolean_function() without arguments returns the function of the gate's first output pin, which is a convenient shorthand for single-output gates.

The variables of these functions are named after the gate's pins (A, B, I0, ...). For some gate types — sequential gates in particular — the function of an output pin is expressed in terms of internal state or other output pins rather than inputs alone, which makes it useless on its own. get_resolved_boolean_function resolves those references so that the returned function depends only on input pins. It takes a GatePin object rather than a pin name:

o_pin = gate.get_type().get_pin_by_name("O")
func = gate.get_resolved_boolean_function(o_pin)

Because pin names repeat across gates, functions of different gates cannot simply be combined. Passing use_net_variables=True names the variables after the connected nets instead, which makes functions from different gates composable:

func = gate.get_resolved_boolean_function(o_pin, use_net_variables=True)

For reconstructing the function of an entire subcircuit rather than a single gate, use the SubgraphNetlistDecorator, see Decorators and the Simple ALU example project.

Configuration data of LUTs and Flip-Flops

On FPGA netlists, LUTs carry a configuration string that determines what they compute, and flip-flops may carry an initial state. HAL stores this in the gate's data container, but the exact category and identifier differ between gate libraries. get_init_data resolves this for you and returns the INIT data of a gate as a list of strings, using the information declared by the gate type's InitComponent (see Gate Type). set_init_data writes it back.

init = gate.get_init_data()   # e.g. ["ABCD1234"] for a LUT4, empty list if the gate has none

Modifying the INIT data of a LUT changes the Boolean function that gate computes — which is exactly what you need for netlist manipulation experiments, and exactly what makes it dangerous to do by accident.

Physical location

If the netlist was parsed from a source that carries placement information, gates may know where they physically sit on the die. has_location reports whether that data is present, get_location returns an (x, y) tuple, and get_location_x / get_location_y return the individual coordinates. The corresponding setters are set_location, set_location_x, and set_location_y.

if gate.has_location():
    x, y = gate.get_location()

Placement information is genuinely useful in reverse engineering: gates belonging to the same word-level structure are frequently placed close together, so physical proximity is an independent hint about logical grouping. Which data entries HAL reads the coordinates from is configured in the gate library via set_gate_location_data_category and set_gate_location_data_identifiers.

Special gates

Each gate library is required to provide dedicated gate types for VCC and GND. Instances of these gate types are connected to constant 1 or 0 signals and need to be marked as such by using mark_vcc_gate and mark_gnd_gate. This process can be reversed using unmark_vcc_gate and unmark_gnd_gate. Whether a gate is a VCC or GND gate can be determined using is_vcc_gate and is_gnd_gate. Marking gates as VCC or GND gates is usually taken care of by the netlist parser and should not be done by the user.

gate.mark_vcc_gate()        # mark a gate as a VCC gate
print(gate.is_vcc_gate())   # prints True
gate.unmark_vcc_gate()      # unmark the VCC gate

By providing the gate as input, all these functions can also be executed on the netlist itself. In addition, calling get_vcc_gates or get_gnd_gates on the netlist returns a list of all VCC or GND gates respectively.

gate = netlist.get_gate_by_id(3)   # get gate with ID 3
netlist.mark_vcc_gate(gate)        # mark gate as VCC gate
netlist.is_vcc_gate(gate)          # returns True
netlist.unmark_vcc_gate(gate)      # unmark gate
netlist.get_gnd_gates()            # return all GND gates

Clone this wiki locally