Tutorial: More on Arrays

This tutorial talks about some of the more advanced ways you can write and organize PHDL quickly. A significant challenge in capturing of a large PCB design has to do with aggregation (busses) of nets. Although tools are becoming more capable in this area (Altium), some tools still do not support declaring a device with arrays of nets (EAGLE). Add to this the challenge of instancing arrays of arrayed devices or hierarchical designs, and we are suddenly dealing assignments of pins to nets in two dimensions. PHDL cleanly handles assignments with a regular array structure, as well as those that are arbitrarily formed.


PHDL allows an arbitrary number of concatenated nets and ports on the right-hand side of any assignment statement. This makes it easy to "break out" an arrayed set of pins of a device into arbitrary signals in the design.

    net[7:0] high_byte, low_byte;
    inst my_device of some_device {
    	// data has been declared [15:0]
        data = high_byte & low_byte;

This example shows how group of pins in a vector (called data) can be assigned to a group of nets. In all cases, assignments take place from left to right order, in the order that they were declared (if no indices have been specified). Therefore, data[15] gets assigned to high_byte[7], and data[0] gets assigned to low_byte[0]. If we want to reverse the ordering, we need to specify it.

        // a different assignment
        data[0:15] = high_byte & low_byte;
        // another different one
        data[0:15] = high_byte[0:7] & low_byte[0:7];
        // equivalent to first
        data[0:15] = low_byte[0:7] & high_byte[0:7];

In Line 2, now data[0] gets assigned to high_byte[7], and data[15] gets assigned to low_byte[0]. Line 5, it is almost the same as the original example except that the byte ordering is reversed. Line 8 is equivalent to the original.

Selecting arbitrary indices is also supported. Instead of specifying an array of bits, just enter an arbitrarily long comma separated list of indices. Vectors may be broken up like this:

        // an arbitrary assignment
        data[3,4,2,6,0,1,7,5] = high_byte;
        data[10,11,12,9,14,8,13,15] = low_byte;

Arbitrary indices may be chosen for each of the right hand side elements as well (high_byte or low_byte). However, all assignments must have equal left and right widths, except for the special case of a replicated connection.


Replication of connections in PHDL was defined in the syntax because of the frequent need to assign one connection to many. This is frequently the case with large arrays where we need to assign a ground connection to all of the ground pins of a device, or a power supply rail to all power pins of a device. To use replication, specify a one-bit wide connection (either a net or a port) in angle brackets on the right hand side.

        // gnds is declared as an array.
        // gnd will get assigned to all gnds pins
        gnds = <gnd>;

There are a few considerations with replications to be aware of. A replicated connection must be only one-bit wide. If the connection was declared as an array, then a single index must be selected from the vector as in:

        // sign-extend the low_byte into the upper bits of data using replication
        data[15:8] = <low_byte[7]>;
        data[7:0] = low_byte;

The Combine Keyword

The combine keyword was mentioned briefly in the tutorial on sub-designs. Its purpose is to construct an array of pins that are common across an instance array, and line them up into a larger array. Without the combine notation, all pins of selected instances are assigned in parallel, which works very well for expressing data busses in very compact notation.

//An array of Memory
device memory_1024x16 {
	//Attributes omitted

	pin[15:0] data;
	pin[9:0] addr;

design myDesign {					
    net[15:0] data_bus;
    net[9:0] addr_bus;
    inst(1:0) memory_array of memory_1024x16 {
    	// all instance pin vectors are assigned in parallel
        data = data_bus;
        addr = addr_bus;

Note that lines 15 and 16 would be the same thing as accessing each pin vector by the qualifier with the appropriate instance index:

        // equivalent to lines 15 and 16 above
        this(1).data = data_bus;
        this(0).data = data_bus;
        this(1).addr = addr_bus;
        this(0).addr = addr_bus;

However, if we wanted to keep the 16 bit wide bus, but use 1-byte wide memory data-paths, thus changing the data pins from the memories from 16 bits wide down to 8 bits wide, we might consider the following syntax:

//An array of Memory
device memory_1024x8 {
	//Attributes omitted

	pin[7:0] data;  //We now have 8 bit wide data outs rather than 16-bit wide
	pin[9:0] addr;

design myDesign {					
    net[15:0] data_bus;
    net[9:0] addr_bus;
    inst(1:0) memory_array of memory_1024x8 {
    	combine(data) = data_bus;  	//Using combine, the two 8-bit data pins can be 
    					//lined up and connected to the 16 bit bus
        addr = addr_bus;			

If no indices are specified anywhere, the order of assignment always takes place in the order the arrayed object was declared. The combine statement above performs the equivalent of the following:

        this(1).data = data_bus[15:8];
        this(0).data = data_bus[7:0];

This can be very useful when using multiple instances of devices or subdesigns and when you desire to use line all of their ports or pins up in a line and connect them to a bus or array of nets.

In short, the combine keyword only works within instance or subinstance arrays. It lines up the specified pins or ports of all the instances in the instance array according to the order used when declaring the instance array. However, instance selection and ordering can be modified through use of the qualifier, (i.e. this).

Note: For an example of arrays and the combine keyword see the Seven Segment Controller example.

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.