Up to now, SKiDL supported hierarchy by applying the `@subcircuit` decorator to a Python function:

``````@subcircuit
def analog_average(in1, in2, avg):
"""Output the average of the two inputs."""

# Create two 1K resistors.
r1, r2 = 2 * Part('Device', 'R', value='1K', dest=TEMPLATE)

# Each input connects thru a resistor to the avg output.
r1[1,2] += in1, avg
r2[1,2] += in2, avg
``````

Then this subcircuit is instantiated by calling the function with nets passed as arguments:

``````in1, in2, in3, in4, out1, out2 = Net()*6  # Make some I/O nets.

analog_average(in1, in2, out1)  # One instantiation of the averager.

... Some more code ...

analog_average(in3, in4, out2)  # A second instantiation.
``````

It was pointed out that this method of instantiating subcircuits is quite different from what is used for parts. Unlike the connections to a subcircuit that are all made at a single place when the function is called, the connections to a part can be placed at various, non-contiguous locations in the code:

``````q_npn = Part("Device", "Q_NPN_BCE")  # Instantiate a transistor.

...

q_npn['E'] += Net('GND')  # Connect the emitter to ground.

...

q_npn['B'] += in1  # Connect an input to the base.

...

q_npn['C'] += out1  # Connect the output to the collector.
``````

Another issue is that you can pass a part instance as an argument to a subcircuit that connects it to the internal circuitry. But you can’t do the same thing with a subcircuit function without making changes to the code because of the syntactic differences in how connections are made:

``````@subcircuit
def analog_average(in1, in2, avg, r):
"""Output the average of the two inputs."""

r1, r2 = r(num_copies=2)  # Create two copies of the resistor part.
r1[1,2] += in1, avg
r2[1,2] += in2, avg

# If r was a subcircuit function, this would have to be written as:
# r(in1, avg)
# r(in2, avg)
``````

To make subcircuits act more like parts, the `@package` decorator has been introduced. Just replace the `@subcircuit` decorator while keeping everything else the same:

``````@package
def analog_average(in1, in2, avg):
r1, r2 = 2 * Part('Device', 'R', value='1K', dest=TEMPLATE)
r1[1,2] += in1, avg
r2[1,2] += in2, avg
``````

Instantiating the subcircuit now occurs in two phases. First, create instances of the subcircuit wherever they are needed:

``````avg1 = analog_average()

...

avg2 = analog_average()
``````

In the second phase, make connections to these subcircuits as if they were parts with the names of the function parameters serving as pin names:

``````in1, in2, in3, in4, out1, out2 = Net()*6

# Make connections. You can use either [] or . to reference the I/O.
avg1['in1'] += in1
avg1.in2    += in2
avg1['avg'] += out1

...

avg2['in1'] += in3
avg2['in2'] += in4
avg2.avg    += out2
``````

In addition to nets, pins, and buses, you can pass any other type of parameter to subcircuits. For example, `analog_average` could take a float as a `ratio` parameter to set the amount each input contributes to the output:

``````@package
def analog_average(in1, in2, avg, ratio):
r = Part('Device', 'R', dest=TEMPLATE)
r1 = r(value=2000 * ratio)
r2 = r(value=2000 * (1-ratio))

r1[1,2] += in1, avg
r2[1,2] += in2, avg
``````

Then the subcircuit can either be instantiated with a given ratio:

``````avg1 = analog_average(ratio=0.25)
avg1.in1 += in1
avg1.in2 += in2
avg1.avg += out1
``````

or you can set the ratio outside the function call:

``````avg2 = analog_average()
avg2.ratio = 0.25  # Use a normal assignment (=) since this is not a circuit connection.
avg2.in1 += in3
avg2.in2 += in4
avg2.avg += out2
``````

A subcircuit function instantiates its circuitry when it is called. But this doesn’t happen when using a package. Instead, the subcircuit is placed in a list and executed after the complete circuit is finalized (i.e., whenever `ERC()` or `generate_netlist()` is called). The arguments passed to the function consist of the connections and other values that were assigned to the package parameters in the preceding code.

That’s about it for the `@package` decorator. Since it’s new, it hasn’t seen a lot of use and there could be unknown bugs lying in wait. If you have questions or problems, please ask on the SKiDL forum or raise an issue.