Tutorial: Designs

In PHDL, a design declaration is the top level block of code that represents a completely encapsulated description of PCB connectivity. By completely encapsulated, we mean that no connectivity is allowed to leave the realm of the design block, just as no traces of copper would ever leave the physical dimensions of the PCB. Because of this, any design declaration block (beginning with the keyword design) may not have any ports declared inside. This is because a "port" by most definitions allows connections across levels of hierarchy -- something we should not be allowed to do at the top level. That said, a typical design declaration might look like this:

// a top-level design declaration
design top {
    // all instances and connectivity of a top level design
    // must remain inside of this block
// no connectivity can exist here.

Designs do not have to have the name "top" and can be anything, but those familiar with hierarchy in HDL's frequently use this notation on a top level hierarchy unit.

Output is Generated Only From Designs

Inside of the plugin, every time a resource (file) is saved that contains any design blocks, new generated output is produced for every design block in the resource and any previously generated output is overwritten. If a design block is in the global namespace (i.e. not declared inside of a package) then the generated output will reside inside the src-gen folder. Every generated file will have the name of the design and appropriate file extension. That relative path structure looks like this:

    <design_name>.asc // the PADS netlist file
    <design_name>.lsi // the layout supplementary info file
    <design_name>.bom // the bill of material file
    <design_name>.rdm // the reference designator mapping file
    <design_name>.scr // the eagle script file
    <design_name>.net // the generic xml netlist file

If the design is declared in a package namespace (packages will be explained in more detail later on in the tutorial), the generated output folder structure would look like this:

    <package_name>_<design_name>.asc // the PADS netlist file
    <package_name>_<design_name>.lsi // the layout supplementary info file
    <package_name>_<design_name>.bom // the bill of material file
    <package_name>_<design_name>.rdm // the reference designator mapping file
    <package_name>_<design_name>.scr // the eagle script file
    <package_name>_<design_name>.net // the generic xml netlist file

For example, if we have a PHDL project called "example" containing a design called "top" which resides in a package called "myPackage", the generated output files would appear in this format:


Ouside of the plugin via the command-line, a src-gen folder will be created in the current directory and will contain exactly this same structure. NOTE: with this format, output file names have absolutely no dependence on input source file names!

What's in a Design?

As mentioned previously, designs contain all the connectivity of a single PCB. Designs may contain any of the following:

The only constraint on ordering of all of these items is that all nets must be declared before they are used. Other than that, all design objects may appear in any order.

Declaring Nets

A net declaration is often just a single line of code expressing the name of a net, its width, and optionally any attributes that the net needs to take on. Currently PHDL does not analyze any net attributes, but this provision was made so they could be analyzed in the future. Some valid net declarations look like this:

// a single net
net a;
// declaring a bunch of nets on one line
net x, y, z;
// declaring an 8-bit net
net[7:0] data;
// declaring multiple net arrays
net[15:0] data, addr;
// declaring nets with attributes
net[1:0] clk {
	attr IMPEDANCE = "50ohms";
	attr NETCLASS = "diffpair";  

Instancing Devices

Inside of a design, devices can be instanced. Instancing a device in PHDL is equivalent to dragging a device from a library in a conventional PCB design tool onto the schematic page. When instancing a device, significantly more context is needed in an example. This is because instancing a device defines two relationships: (1) which device the instance inherits all pins from, and (2) defines how all of the pins are to be connected to the rest of the design. Instancing just one device for an example does not accomplish much. Instead, we instanced four devices in our design to make a very simple RC filter with two I/O pin-headers. The listing below shows how this is done in PHDL:

 * Device Library (required attributes omitted)
// a resistor
device resistor {
    pin a = {1};
    pin b = {2};

// a polarized capacitor
device capacitor {
    pin pos = {+};
    pin neg = {-};

device hdr_2x1 {
    pin[1:0] p = {p1, p0};

 * A simple RC filter that uses devices above
design top {
    net in, out, gnd;
    // an instance of a resistor
    inst my_res of resistor {
        a = in;
        b = out;
    // an instance of a polarized capacitor
    inst my_cap of capacitor {
        pos = out;
        neg = gnd;
    // the input and output connectors
    // I/O is on p[1], and gnd is on p[0]
    inst(1:0) my_io of hdr_2x1 {
        this(1).p[1] = in;
        this(0).p[1] = out;
        p[0] = gnd;

The resistor and capacitor instance should nearly be self explanatory. Line 28 declares an instance with the keyword inst , and an instance name called my_res . After that is the name of the device that the instance is based off of, in this case the resistor in the library above. The reference is formed by typing the actual name of the device after the keyword of . The capacitor has exactly the same syntax. The connector instance is declared as an array (1:0) which we will talk about later.

Disclaimer: We are purposely not showing an equivalent schematic here, so that you can begin to start thinking in terms of what PHDL is describing textually. If you need to sketch the connectivity of an RC filter, please follow along, and perhaps draw the equivalent circuit on some scratch paper. Or, click here for an article describing the circuit if you are not familiar with it.

Pin Assignments

Pin assignments form the connectivity at the lowest level, i.e. the primitive device level. The left side has a reference to a pin name in the device declaration, and the right side can have an arbitrary number of references to nets inside of the design, each separated by the concatenation operator, "&" (more on that later). The resistor pin a is assigned to the in net on Line 29 . Its pin b and also the capacitor pin pos are assigned to the out net on Line 30 and 35 . These assignments effectively connect the two pins of the different devices together.

The most interesting pin assignments can be made inside of an array of instances, as demonstrated by the input and output connector instances. Note that the instance my_io has been declared as an array of two hdr_2x1 devices. They are uniquely qualified with indices from the array declaration (1:0) so we effectively have a my_io(1) and a my_io(0) to work with. Each of these connectors have two pins, so we need to assign a total of 4 pins somewhere in the body of the my_io instance, otherwise we will get unassigned pin errors reported by the tool. To access a particular instance in the array, we use the this() syntax, with an index of the array as the argument. We call this notation a "qualifier" because it qualifies or selects only certain pins of the instance array to be assigned. On Line 42 we have selected only instance my_io(1) 's pin p[1] and assigned it to the net in . Previously, we had also assigned my_res 's pin a to the net in , so this connector is now tied into the resistor. We perform a similar assignment on the output connector on Line 43. On Line 44 we have the general case where we have not specified any instance indices with a qualifier. The language assumes that if we don't specify indices anywhere that we are implying that we access all of them. So, both instances my_io(1) 's and my_io(0) 's p[0] pins are connected to the gnd net.

Note that Line 44 could have been assigned in the following way:

this(1).p[0] = gnd;
this(0).p[0] = gnd;

Attribute Assignments

Attributes from the device declaration may also be overridden inside of the instance block. In the previous example, we left out the attributes to only show the connectivity. Here we illustrate how you can re-assign an attribute to customize resistor values of a voltage divider circuit.

device resistor {
    attr REFPREFIX = "R";
    attr FOOTPRINT = "R0603";
    attr LIBRARY = "rcl-smd";
    attr VALUE = "1k";
    pin a = {1};
    pin b = {2};

design top {

    net vcc, gnd, ref;
    // a simple voltage divider with two resistors between the three above nets
    inst(1:0) my_res of resistor {
        this(1).VALUE = "2k";
        this(1).a = vcc;
        this(1).a = gnd;
        b = ref;
    // ... more of the design down here

The voltage divider is configured such that the upper resistor leg is set to 2k, while the lower leg remains at 1k (from the declaration). Therefore, the voltage present on ref will be 1/3 of the voltage of vcc with respect to gnd .

Additional attributes may also be declared directly inside of instances. Note: an attribute declared within an instance array will be inherited by all instances.

    inst(1:0) my_res of resistor {
        attr TOLERANCE = "5%";


In this tutorial, we have shown different ways that nets can be declared, and how devices are instanced and connected together with nets. We have introduced some basic ways that nets can be assigned. In the next tutorial, we will show how you can create design units that can be reused in hierarchy. In PHDL, we call these subdesigns .

Getting Started With PHDL

The best place to start is to visit our installation instructions which will help you get PHDL up and running on your machine. Then, be sure to visit the tutorial page.