Skip to content

Netlist

julianspeith edited this page Aug 12, 2026 · 25 revisions

A netlist comprises gates and nets and describes the connectivity of an electronic circuit. In hardware reverse engineering, a netlist may either be extracted from the images taken of each layer of an integrated circuit (IC) or from the bitstream of a field-programmable gate array (FPGA). The netlist itself can be interpreted as a graph with the gates representing its nodes and the nets representing their connections. Commonly, the netlist is stored as a Verilog or VHDL file. It can be parsed into HAL by using either the provided netlist parsers or writing a new one.

In HAL, the netlist is the root object: it owns all gates, nets, modules, and groupings, and it is the only way to create or delete them. From within Python, the currently opened netlist is always available as the variable netlist.

Furthermore, HAL provides the reverse engineer with the ability to add structure to the netlist during reversing. To this end, modules provide an easy way for modularization and hierarchization of the design. Additionally, groupings can be used to temporarily store unordered sets of gates, nets, and modules during the reversing process.

Netlist information

In HAL, a netlist holds its ID, the path to the file the netlist has been read from, the name of the design, and the name of the targeted device. Additionally, it holds information about the underlying gate library and keeps track of all its gates, nets, modules, and groupings. The ID can be retrieved using get_id and set using set_id. To get or set the path to the input file, use get_input_filename or set_input_filename respectively. Access to the design name and target device is provided by get_design_name, set_design_name, get_device_name, and set_device_name. The gate library can be retrieved using get_gate_library.

id = netlist.get_id()                     # get the id of the netlist
name = netlist.get_design_name()          # get the name of the design
netlist.set_device_name("example_chip")   # set the name of the target device
gl = netlist.get_gate_library()           # get the underlying gate library

The gate library is fixed when the netlist is created and cannot be exchanged afterwards — it defines what every gate in the netlist means, see Gate Library.

A full independent copy of a netlist can be created using copy. This is useful whenever you want to try a destructive transformation (removing buffers, replacing gates, flattening hierarchy) without losing the original.

nl_copy = netlist.copy()   # create a deep copy of the netlist

Operating on netlist elements

Gates, nets, modules, and groupings are technically part of the netlist and may be managed through functions provided by the netlist. These functions enable the creation, deletion, and retrieval of gates, nets, modules, and groupings. They are described in more detail below. Note that whenever an asterisk * is present within the description, the keywords gate, net, module, or grouping may be used in its place.

A new element of the netlist may be created using create_* with the parameters being different for each kind of element. Calling this function will create the respective element and assign a unique ID to it such that it can be unambiguously identified by the ID. Further, an element can be deleted from the netlist using delete_* which always takes the element itself as input. To check whether an element is part of the netlist, the function is_*_in_netlist may be used. A single element can be retrieved from the netlist by calling get_*_by_id and providing the element's unique ID. A list of elements of the same kind can be requested from the netlist using get_*s. To narrow down the returned elements, a filter may be applied in form of a lambda function.

gate_1 = netlist.create_gate(example_type, "example")                          # create a new gate of type 'example_type' going by the name 'example'
print(netlist.is_gate_in_netlist(gate_1))                                      # prints True as the gate is part of the netlist
net_1 = netlist.get_net_by_id(10)                                              # retrieves the net with ID 10 from the netlist
lut6_gates = netlist.get_gates(lambda g : g.get_type().get_name() == "LUT6")   # retrieves all gates of the gate type with name 'LUT6'
netlist.delete_module(netlist.get_module_by_id(3))                             # removes module with ID 3 from the netlist

Filtering with a lambda is the workhorse of netlist analysis. A few patterns you will use constantly:

# all flip-flops in the design
ffs = netlist.get_gates(lambda g: g.get_type().has_property(hal_py.GateTypeProperty.ff))

# all gates that are not buffers or inverters
logic = netlist.get_gates(lambda g: g.get_type().get_name() not in ["BUF", "INV"])

# all nets that fan out to more than 20 gates -- often clock or reset signals
high_fanout = netlist.get_nets(lambda n: n.get_num_of_destinations() > 20)

Element IDs

Every element carries a numeric ID that is unique among elements of its kind — a gate and a net may both have ID 5. IDs are stored in the .hal file and remain stable across saving and reloading, which makes it safe to hardcode them in analysis scripts, see Using HAL.

If you create elements programmatically and need control over the IDs being handed out, the netlist exposes get_unique_*_id (the next ID that would be assigned), get_used_*_ids (all IDs currently in use), and get_free_*_ids (IDs of deleted elements that are available for reuse), each with a corresponding setter. These are mainly relevant when writing parsers or netlist transformations that must produce deterministic IDs.

VCC and GND gates

As VCC and GND gates are handled a bit differently in HAL and are generally considered to be the source of constant 1 and 0 nets, HAL offers some designated functions to check for and operate on GND and VCC gates. In the following, the asterisk * may be replaced with either gnd or vcc.

Using is_*_gate, one can check whether the provided gate is internally considered to be a VCC or GND gate. To retrieve all GND or VCC gates within the netlist, the function get_*_gates may be used. Note that the user can also mark and unmark GND and VCC gates using mark_*_gate and unmark_*_gate. However, this functionality should be used carefully as it can lead to inconsistencies otherwise.

netlist.is_vcc_gate(some_gate)            # returns True for a VCC gate
gnd_gate_list = netlist.get_gnd_gates()   # returns a list of all GND gates

The nets driven by these gates carry constant values throughout the design. They can be retrieved directly using get_gnd_nets and get_vcc_nets. Recognizing them matters in practice: a LUT input tied to a constant effectively reduces the LUT's function, and an enable pin tied to VCC means the gate is permanently enabled.

const_nets = netlist.get_gnd_nets() + netlist.get_vcc_nets()   # all constant nets

Global input and output nets

Global input and output nets to the netlist are internally annotated accordingly to make them distinguishable from purely internal nets. These nets correspond to external inputs or outputs to the netlist and may hence not have a source or destination. In the following, the asterisk * may be replaced with either input or output.

To check whether a net is a global input or output, is_global_*_net may be called. All nets of the netlist that are annotated as global inputs or outputs may be accessed as a list using get_global_*_nets. Again, the user can mark and unmark global inputs and outputs on their own using mark_global_*_net and unmark_global_*_net, but should be careful when doing so.

netlist.is_global_input_net(some_net)                # returns True for a global input net
global_out_list = netlist.get_global_output_nets()   # returns a list of all global output nets

Global inputs and outputs are the boundary of the design, and hence the natural starting points for analysis: they are the only nets whose meaning you can often guess from the outside.

Top module

The top module, i.e., the module that contains all gates of the netlist, either directly or in sub-modules, can be retrieved using get_top_module. It always has ID 1 and is created together with the netlist, so it always exists.

top_mod = netlist.get_top_module()   # returns the top module

Clone this wiki locally