Skip to content
SJulianS edited this page Nov 16, 2022 · 26 revisions

A module is a functional unit comprising gates and their connected nets. Modules allow for hierarchization of the netlist and can be nested into each other, thus creating different levels of abstraction from the original netlist. Per design, modules only really contain gates and not nets. An important distinction between gates and modules is that a net does not end at the module boundaries but actually crosses them. The module comes with a set of functions to obtain all nets that are connected to gates within that module. By default, all gates of a netlist are contained in a single module called the "top_module" if no hierarchy is defined within the netlist file. Each module comes with a unique ID and a name. Additionally, a type may be assigned to a module if desired.

Module Information and Management

A module can be created by calling create_module on the current netlist. When creating a new module, the user always needs to specify its parent module. In the example below, we simply use the "top_module" as a parent module. The ID cannot be changed by the user and may only be read by calling the get_id function from Python. Read and write access to the name is provided by the get_name and set_name functions respectively. Similarly, the module's type can be accessed using get_type and set_type. The get_top_module function can be executed on the netlist and returns the top module of the current netlist. Some example Python code is given below:

m = netlist.create_module("example_module", netlist.get_top_module())   # create a new module within the top module
print(m.get_name())                                                     # print the name of the module
print(m.get_type())                                                     # print the type of the module
print(m.get_id())                                                       # print the ID of the module

To retrieve a module from the netlist that corresponds to a known ID, the get_module_by_id command can be executed on the netlist. Furthermore, a module may be deleted using delete_module as shown below. When deleting a module, all gates assigned to that module are handed over to its parent module.

m = netlist.get_module_by_id(3)   # get the module with ID 3 from the netlist
netlist.delete_module(m)          # delete the module

In addition, a module can also be part of a grouping itself. While details on how to assign a grouping can be found here, the easiest way to retrieve the current grouping of a module is given by the get_grouping function.

m = netlist.get_module_by_id(3)   # get the module with ID 3
grouping = m.get_grouping()       # get the current grouping of the module

Managing Gates, Parent-, and Submodules

Gates can be added to a module by calling assign_gate and removed by using remove_gate. Furthermore, the user can check whether a gate or module is part of a module by resorting to contains_gate and contains_module. Finally, a list of contained gates and submodules is returned by the functions get_gates and get_submodules. A short example is given below:

m = netlist.create_module("example_module", netlist.get_top_module())   # create a new module within the top module
m.assign_gate(netlist.get_gate_by_id(1))                                # assign gate with ID 1 to module
m.contains_gate(netlist.get_gate_by_id(4))                              # return 'false'
gates = m.get_gates()                                                   # return a list containing only the gate with ID 1
m.remove_gate(gates[0])                                                 # remove gate with ID 1 from module

A module's parent module can be obtained using get_parent_module. For the top module, this will return None. Furthermore, the gates of a module may also be accessed by ID using get_gate_by_id.

top = netlist.get_top_module()                     # get the top module of the netlist
m = netlist.create_module("example_module", top)   # create a new submodule of the top module 
m.get_parent_module()                              # return the top module
top.get_parent_module()                            # return None
top.get_gate_by_id(4)                              # get gate with ID 4 from top module if present within the module

Managing Nets

The nets contained in a module are dynamically assigned depending on the gates belonging to the module. Nets can either be inputs, outputs, or internal to the module. Note that a net may also fall into multiple of these categories at the same time. Input nets are all nets that have a destination within the module and are either a global input to the netlist or have at least one of their sources outside of the module. Similarly, output nets have a source within the module and are either a global netlist output or have at least one destination outside of the module. Internal nets do have at least one source and one destination within the module, but may have additional outside connections. Hence, a net with sources and destinations both inside and outside of the module is regarded as input, output, and internal. To access those nets, the functions get_input_nets, get_output_nets, and get_internal_nets each return a respective list of nets. To get all nets that have at least one source or one destination within the module, get_nets may be used. Additionally, to check whether a net is an input, output, or internal net, the functions is_input_net, is_output_net, is_internal_net are provided.

nets = m.get_nets()                     # get all nets of the module
input_nets = m.get_input_nets()         # get all input nets of the module
internal_nets = m.get_internal_nets()   # get all internal nets of the module

Managing Pins

Similar to gate types, modules also feature pins through which they are connected to surrounding circuitry. Each ModulePin is assigned an ID unique within the owning module, a name, a direction, a type, and a net. Usually, module pins are automatically assigned whenever gates are added or removed to/from the respective module. Manual creation of module pins is usually not desirable and hence strongly discouraged. However, names and types of the pins may be changed on demand.

The pins of a module can be retrieved using get_pins by optionally providing a filter function. If only the names of the pins are required, get_pin_names may be used. Similar short-hand functions are provided to only retrieve input or output pins (including inout pins): get_input_pins, get_input_pin_names, get_output_pins, and get_output_pin_names.

pins = m.get_pins()                                                     # get all pins of module m
input_pins = m.get_input_pins()                                         # get input pins of module m
data_pins = m.get_pins(lambda p: p.get_type() == hal_py.PinType.data)   # get pins of type 'data' of module m

Furthermore, pins can be retrieved using their ID, name, or the associated net using get_pin_by_id, get_pin_by_name, and get_pin_by_net.

pin_by_id = m.get_pin_by_id(3)            # get pin with ID 3 of module m
pin_by_name = m.get_pin_by_name("A")      # get pin with name "A" of module m
some_net = netlist.get_net_by_id(5)       # get net with ID 5
pin_by_net = m.get_pin_by_net(some_net)   # get pin associated with net with ID 5 of module m

The ID, name, direction, type, and net associated with a pin can be retrieved using get_id, get_name, get_direction, get_type, and get_net of class ModulePin respectively. To change the name or type of a module pin, use set_pin_name and set_pin_type provided by the Module class.

some_pin = m.get_pin_by_id(1)              # get pin with ID 1 of module m
pin_name = some_pin.get_name()             # get the name of the pin
pin_direction = some_pin.get_direction()   # get the direction of the pin
pin_type = some_pin.get_type()             # get the type of the pin
m.set_pin_name(some_pin, "new name")       # set the name of the pin to "new name"

(TODO) Managing Pin Groups

A so called ModulePinGroup is used to enable multi-bit pins. On creation, every pin is automatically assigned to a pin group that contains just that very pin. If needed, this can be prevented using the create_group flag of the create_pin function. However, in the end HAL requires each pin to be part of a group to avoid memory inconsistency. Hence, when disabling auto-assignment to a pin group on pin creation, the user must ensure that the respective pin is manually assigned to a pin group later on.

Pin groups can be created using create_pin_group and providing a name for the group and an ordered list of pins to be assigned to the group. Optionally, a direction, type, order (ascending or descending), and start_index can be provided. Pins can then be accesses either directly or through their group. All pin groups of a module can be accessed using get_pin_groups. As for module pins, pin groups can also be retrieved by their unique ID or name using get_pin_group_by_id and get_pin_group_by_name. The class ModulePinGroup offers functions to retrieve ID (get_id), name (get_name), direction (get_direction), type (get_type), order (is_ascending), and start index (get_start_index) of a pin group. The pins of the group are returned by get_pins. To get a pin at a certain index of the group, get_pin_at_index may be used. To get the index of a given pin within a group, the function get_index is provided.

m.create_pin_group("A", [a0, a1], hal_py.PinDirection.input)   # assign both pins to a new pin group with name "A"
groups = m.get_pin_groups()                                    # get all pin groups of the gate type
a = m.get_pin_group_by_name("A")                               # get pin group named "A" of the gate type
pins = a.get_pins()                                            # get all pins belonging to group "A" of the gate type as an ordered list

The class Module provides additional functions to facilitate more advanced options to handle pin groups. Module pin groups can be deleted using delete_pin_group, which will result in every pin previously belonging to the group being assigned to a new pin group only containing that one pin. In addition, pins may also be assigned to a group after creation of the group using assign_pin_to_group. The order of pins within a group can be changed using move_pin_within_group. Finally, a pin may be removed from a group using remove_pin_from_group. Note that the indices within a pin group are always continuous. Removing a pin will cause the index gap being closed by all subsequent pins resulting in their indices shifting by 1.

some_group = m.get_pin_group_by_id(3)            # get pin group with ID 3 of module m; assume the group to contain pins "A(0)", "A(2)", and "A(3)"
pin_a1 = m.get_pin_by_name("A(1)")               # get the pin "A(1)" currently missing from the pin group
m.assign_pin_to_group(some_group, pin_a1)        # assign "A(1)" to the pin group; it will be appended to the back of the group
m.move_pin_within_group(some_group, pin_a1, 1)   # move pin to correct position within group
pin_a3 = m.get_pin_by_name("A(3)")               # get the pin "A(3)" currently belonging to the pin group
m.remove_pin_from_group(some_group, pin_a3)      # remove "A(3)" from the pin group
m.delete_pin_group(some_group)                   # delete the entire pin group
Clone this wiki locally