Skip to content

Gate Type

SJulianS edited this page Oct 19, 2022 · 51 revisions

A gate type describes the functionality of a standard cell and is always associated with a gate library. Each gate within a netlist represents an instance of a gate type. Special properties (GateTypeProperty) can be assigned to a gate type to describe its general functionality and facilitate automated analysis. A single gate type might have multiple properties depending on its purpose. Available base types are:

  • combinational: a combinational gate type.
  • sequential: a sequential gate type.
  • power: a gate type that is connected to a power/voltage source.
  • ground: a gate type that is connected to ground.
  • lut: a LUT gate type generating its output from an initialization string.
  • ff: a FF gate type generating its outputs from its internal state while adhering to a clock.
  • latch: a latch gate type generating its outputs from its internal state whenever enabled.
  • ram: a RAM gate type storing large amounts of data that can be accessed by providing an address.
  • io: an IO gate type that takes care of the global in- and outputs to the chip.
  • dsp: a DSP gate type that accelerates the computation of some mathematical operations.
  • mux: a MUX gate type choosing one of many input signals depending on one or more select inputs.
  • buffer: a (potentially tri-state) buffer gate type.
  • carry: a carry gate type implementing some form of carry logic.

In addition to its functionality, the gate type also describes the connectivity of a gate by declaring I/O pins. Each pin must be assigned a direction (PinDirection), available are:

  • input: an input pin enables a gate to operate on external signals.
  • output: an output pin generates a signal depending on the gate type's functionality and the applied inputs.
  • inout: an inout pin is both an input and an output pin at the same time.
  • internal: an internal pin cannot have any connections outside of a gate; it is not fully supported by HAL.

Finally, each pin can also be assigned a type (PinType) to easily distinguish between, e.g., clock and data pins. Available pin types are:

  • none: default pin type.
  • power: outputs a constant 1.
  • ground: outputs a constant 0.
  • lut: generates output dependent on LUT configuration.
  • state: generates output from internal state of a sequential gate type.
  • neg_state: generates output from negated internal state of a sequential gate.
  • clock: clock input or output.
  • enable: input that enables or disables a sequential gate.
  • set: input setting the internal state of a sequential gate to 1 when active.
  • reset: input setting the internal state of a sequential gate type to 0 when active.
  • data: input or output through which data is fed to or from a gate.
  • address: input or output through which one bit of an address is provided.
  • io_pad: acts as IO to the netlist.
  • select: select between multiple inputs of a MUX.

The GateType class itself only provides basic functionality such as storing a gate type's name, pins, properties, and generic Boolean functions. A gate type's functionality can be extended by the use of so called GateTypeComponents, which add support for, e.g., flip-flops and LUTs. Currently, HAL features the following components, which are described in more detail towards the end of this page:

  • lut: support for LUT configuration strings.
  • ff: deals with clock, state transitions, and asynchronous set and reset.
  • latch: deals with enable, state transitions, and asynchronous set and reset.
  • ram: describes RAM data.
  • init: points to initialization data for, e.g., LUTs, FFs, or RAMs.
  • state: deals with the internal state of a sequential gate.
  • ram_port: handles address-to-data mapping for a RAM port.

Gate Type Information

Creation of new gate types is currently not fully supported for Python, but is limited to the C++ API only. However, the properties of existing gate types can be accessed and manipulated using designated functions. The ID and name of a gate type can be retrieved using get_id and get_name respectively. Both ID and name cannot be changed by the user. The properties of a gate type are returned by using get_properties, new ones may be added using assign_property. To simply check whether a gate type is of a certain property, has_property may be used. Furthermore, the gate library that a gate type is assigned to can be retrieved using get_gate_library.

gl = netlist.get_gate_library()                      # get the current gate library
gt = gl.get_gate_type_by_name("some_gt")             # retrieve gate type with name "some_gt"
id = gt.get_id()                                     # get ID of gate type
name = gt.get_name()                                 # get name of gate type
properties = gt.get_properties()                     # get all properties of gate type
gt.assign_property(hal_py.gateTypeProperty.buffer)   # assigns "buffer" property
gt.has_property(hal_py.GateTypeProperty.buffer)      # returns "True"
gl = gt.get_gate_library()                           # returns the gate library

Two gate types may be evaluated for equality using the == and != operators. Currently, equality is determined only using a gate type's ID and gate library.

(OUTDATED) Managing Pins

Each gate type comes with at least one pin to facilitate connections between gates. As mentioned above, a pin always has one of four directions (PinDirection). Furthermore, each pin can be assigned a pin type (PinType) in case it implements some special functionality.

Pins can be added using the add_pin and add_pins functions by providing at least a pins name and direction. Optionally, the pin type can also be specified and will be set to none otherwise. For legacy reasons, the functions add_input_pin, add_input_pins, add_output_pin, and add_output_pins are also still available. In order to retrieve a list of all pins in the exact order they have been added to the gate type (i.e., when parsing them from a standard cell library), the function get_pins may be used. Again, for backwards compatibility the functions get_input_pins or get_output_pins may also be called.

gt.add_pin("A", hal_py.PinDirection.input)                           # add an input pin called "A"
gt.add_pin("B", hal_py.PinDirection.input)                           # add an input pin called "B"
gt.add_pin("S", hal_py.PinDirection.input, hal_py.PinType.select)    # add an input select pin called "S"
gt.add_pin("O", hal_py.PinDirection.output)                          # add an output pin called "O"
out_pins = gt.get_pins()                                             # get all pins as a list

Furthermore, multiple functions are provided to interact with and manipulate a pin's direction and type. Using assign_pin_type, a pin's type can be changed at any time. Note that it is not possible to change a pin's direction after creation of the pin. By calling get_pin_direction or get_pin_type one can retrieve the direction or type of a single pin. Calling get_pin_directions or get_pin_types returns a dictionary from all pins of a gate type to their directions or types. Finally, get_pins_of_direction and get_pins_of_type can be used to get all pins of a desired direction or type.

gt.assign_pin_type("D", hal_py.PinType.data)                       # assign type 'data' to pin "D"
dir = gt.get_pin_direction("A")                                    # get the direction of pin "A"
type = gt.get_pin_type("A")                                        # get the type of pin "A"
direction_dict = gt.get_pin_directions()                           # get the mapping from pins to direction
type_dict = gt.get_pin_types()                                     # get the mapping from pins to type
input_pins = gt.get_pins_of_direction(hal_py.PinDirection.input)   # get all input pins off the gate type
address_pins = gt.get_pins_of_direction(hal_py.PinType.address)    # get all address pins off the gate type

(OUTDATED) Managing Pin Groups

So called pin groups are used to facilitate multi-bit pins. Before being able to set up such a group, the respective pins of that group need to be created using the previously mentioned functions. Then, pin groups can be created using assign_pin_group respectively. The function expects a name for the group and a map from index to pin name as input. The individual pins keep their name but can be addressed using the group name and their index during netlist parsing. All available pin groups of a gate type can be accessed using get_pin_groups. To get all pins of a group, one can call get_pins_of_group to retrieve a map from index to pin name containing all pins assigned to the specified group. Using get_pin_group, the user can request the group that a given pin belongs to.

gt.add_pin("A(0)", hal_py.PinDirection.input)        # add an input pin called "A(0)"
gt.add_pin("A(1)", hal_py.PinDirection.input)        # add an input pin called "A(1)"
gt.assign_pin_group("A", {0 : "A(0)", 1 : "A(1)"})   # assign both pins to a pin group with name "A"
groups = gt.get_pin_groups()                         # get all pin groups of the gate type
pins = gt.get_pins_of_group("A")                     # get all pins belonging to group "A" as well as their indices
group_name = gt.get_pin_group("A(0)")                # returns "A"

(OUTDATED) Boolean Functions

A gate type should feature one Boolean function for each of its output pins to describe the output's value as a function of its inputs. A Boolean function may be added using add_boolean_function by providing a name for the function and the Boolean function itself. The variables of the Boolean function should correspond to the names of the input pins of the gate. To get all Boolean function of a gate type, get_boolean_functions should be used.

gt.add_pin("A", hal_py.PinDirection.input)         # create input pin "A"
gt.add_pin("B", hal_py.PinDirection.input)         # create input pin "B"
gt.add_pin("O", hal_py.PinDirection.output)        # create output pin "O"
bf = hal_py.BooleanFunction.from_string("A & B")   # create a new Boolean function representing an AND
gt.add_boolean_function("O", bf)                   # add the Boolean function named after the output pin

Gate Type Components

A gate type may comprise none, one, or multiple nested components that extend the gate type's functionality. Currently, HAL provides components of the following types (ComponentType):

  • lut: holds information on whether the LUT's configuration string is read in ascending or descending order. Must be used in combination with an init component.
  • ff: provides Boolean functions for the clock, next state, and asynchronous set and reset. Must be used in combination with a state component and may be extended using an init component.
  • latch: provides Boolean functions for the enable, data in, and asynchronous set and reset. Must be used in combination with a state component.
  • ram: holds the size of a RAM in bits. Must be used in combination with one or multiple ram_port components and may be extended using an init component.
  • init: stores the data category and a list of identifiers at which initialization data for a gate of the respective gate type is located.
  • state: keeps identifiers for the internal state and negated internal state of sequential gates.
  • ram_port: provides information on a single port of a RAM, such as Boolean functions for clock and enable, identifiers for address and data pin groups, and a flag to determine whether port provides read or write access.

All components of a gate type can be retrieved using get_components. This function also takes an optional filter allowing the user to narrow down the resulting list of components. An empty list is returned ion the filter does not match any components. If just a single component is desired, get_component. may be used in combination with a respective filter. If the filter specified for the latter function matches none or multiple components, None will be returned. Additionally, is_class_of can be used to determine whether a given component is of a desired type. Finally, has_component_of_type returns true only if the gate type has a component of the specified type.

all_components = gt.get_components()   # get all components of the gate type
lut_components = gt.get_components(lambda c: hal_py.LUTComponent.is_class_of(c))   # get all components of type `lut` (commonly only a single one)
lut_component = get_component(lambda c: hal_py.LUTComponent.is_class_of(c))        # get a component of type `lut`
has_component_of_type(hal_py.GateTypeComponent.ComponentType.lut)                  # True for LUT gate types

Below we reflect on the most common gate types and go into more details on the components they make use of.

LUT Gate Types

It is within the nature of LUTs that their implemented functionality varies depending on the configuration string provided to each LUT during boot up. Thus, LUT gates do not all implement the same function.

Hence, LUT gate types need to implement some logic to deal with configuration strings that are read from the netlist file during parsing. For most devices devices, the initialization of a LUT usually ends up in the generics of the respective gate instance. Thus, we expect such data to be stored within the data container functionality inherited by each gate. While the exact location of these information is stored within an InitComponent, the order in which the configuration string is supposed to be interpreted its determined by a LUTComponent.

While the function InitComponent.get_init_category returns the data category, InitComponent.get_init_identifier returns a list of data identifiers. For LUTs, this list comprises only a single entry. Hence, these information are used to set up the search path that points onto each gate's configuration string within its data container. The order in which the configuration string is supposed to be read can be retrieved using LUComponent.is_init_ascending. For the Xilinx UNISIM gate library, this looks as follows:

lut_comp = lut_gt.get_component(lambda c: hal_py.LUTComponent.is_class_of(c))
init_comp = lut_gt.get_component(lambda c: hal_py.InitComponent.is_class_of(c))
init_comp.get_init_category()     # returns "generic"
init_comp.get_init_identifiers()  # returns ["INIT"] 
lut_comp.is_init_ascending()      # returns True

Note that only output pins of type 'lut' generate their Boolean function from the respective configuration string.

Flip-Flop Gate Types

Flip-flops are clocked sequential gates that keep an internal state based on the input values being applied on a (commonly) positive clock edge. Their outputs are generated based on their current internal state. In HAL, the internal state is computed based on a next_state function. While synchronous set and reset can be realized using this next_state function, two designated functions are used for asynchronous set (async_set) and reset (async_set). Clock enable and inverted clocks may be realized using the clock function. Whenever clock evaluates to True, the internal state is set to the result of the next_state function. Furthermore, input pins carrying a clock signal must be declared clock pins by assigning pin type clock. This in required in order to facilitate netlist simulation.

The described functionality is implemented within the FFComponent that holds all required Boolean functions and supplementary information. The functions can be accessed using FFComponent.get_clock_function, FFComponent.get_next_state_function, FFComponent.get_async_set_function, and FFComponent.get_async_set_function. Additionally, the behavior that the flip-flop exhibits when both async_set and async_reset are applied at the same time can be retrieved using FFComponent.get_async_set_reset_behavior. The function returns a tuple of two values, the first one describing the behavior for the internal state, the second one the behavior for the negated internal state. The following behaviors are allowed:

  • L: the state is set to 0.
  • H: the state is set to 1.
  • N: the state remains unaffected.
  • T: the state is toggled, i.e., a 0 becomes a 1 and a 1 becomes a 0.
  • X: the state is undefined.

An additional StateComponent holds identifiers for both the internal state as well as the negated internal state. These identifiers may be used within any of the Boolean functions describing output, clock, next state, or asynchronous set and reset to reference the current internal state. Hence, StateComponent.get_state_identifier and StateComponent.get_neg_state_identifier provide access to these identifiers.

ff_comp = ff_gt.get_component(lambda c: hal_py.FFComponent.is_class_of(c))
state_comp = ff_gt.get_component(lambda c: hal_py.StateComponent.is_class_of(c))
ff_comp.get_clock_function()              # returns the clock function
ff_comp.get_next_state_function()         # returns the next_state function
ff_comp.get_async_set_function()          # returns the asynchronous set function
ff_comp.get_async_reset_function()        # returns the asynchronous reset function
ff_comp.get_async_set_reset_behavior()    # returns a tuple describing the behavior when both asynchronous set and reset are active
state_comp.get_state_identifier()         # returns the identifier for the internal state
state_comp.get_neg_state_identifier()     # returns the identifier for the negated internal state

Additionally, within FPGA netlists flip-flops may come with pre-initialized values that are loaded into the flip-flops' internal state during startup. Similar to LUT gate types, an InitComponent may be used to realize this behavior. For the Xilinx UNISIM gate library, this looks as follows:

init_comp = ff_gt.get_component(lambda c: hal_py.InitComponent.is_class_of(c))
init_comp.get_init_category()     # returns "generic"
init_comp.get_init_identifiers()  # returns ["INIT"] 

Latch Gate Types

Latches are not controlled by a clock and simply emit their state whenever they are enabled. Hence, as soon as their enable function evaluates to True, the output of the data_in function is written to the internal state. Similar to flip-flops, latches additionally come with asynchronous set and reset functionality provided by the async_set and async_reset functions.

The described functionality is implemented within the LatchComponent that holds all required Boolean functions and supplementary information. The functions can be accessed using LatchComponent.get_enable_function, LatchComponent.get_data_in_function, LatchComponent.get_async_set_function, and LatchComponent.get_async_set_function. Additionally, the behavior that the latch exhibits when both async_set and async_reset are applied at the same time can be retrieved using LatchComponent.get_async_set_reset_behavior. The function returns a tuple of two values, the first one describing the behavior for the internal state, the second one the behavior for the negated internal state. Allowed values are equal to the flip-flop case.

An additional StateComponent holds identifiers for both the internal state as well as the negated internal state. These identifiers may be used within any of the Boolean functions describing output, enable, data in, or asynchronous set and reset to reference the current internal state. Hence, StateComponent.get_state_identifier and StateComponent.get_neg_state_identifier provide access to these identifiers.

latch_comp = latch_gt.get_component(lambda c: hal_py.LatchComponent.is_class_of(c))
state_comp = latch_gt.get_component(lambda c: hal_py.StateComponent.is_class_of(c))
latch_comp.get_enable_function()             # returns the enable function
latch_comp.get_data_in_function()            # returns the data_in function
latch_comp.get_async_set_function()          # returns the asynchronous set function
latch_comp.get_async_reset_function()        # returns the asynchronous reset function
latch_comp.get_async_set_reset_behavior()    # returns a tuple describing the behavior when both asynchronous set and reset are active
state_comp.get_state_identifier()            # returns the identifier for the internal state
state_comp.get_neg_state_identifier()        # returns the identifier for the negated internal state
Clone this wiki locally