sch-rnd hierarchic design

This is an user guide; the formal specification of the feature is documented in the design doc as feature design and implementation notes .

After a brief intro, this document splits use cases in three categories. The basic category contains the most trivial use cases with an intro on how to set up a hierarchic design. The intermediate category contains more advanced approaches, still limiting addressing to two different types. Finally the expert category discusses usage that exploits the full potential of sch-rnd but requires deeper understanding.

Note: all examples are multisheet with an explicit project file so project.lht needs to be loaded in sch-rnd.

Non-hierarchic cases

A non-hierarchic design has a single sheet or a set of sheets, with or without a project file. The latter is also called flat multisheet design.

In a non-hierarchic case every component and network are in a single project global namespace. In practice this means:

Hierarchic design intro

In a hierarchic design a schematic sheet contains a symbol that represents a subsheet. This symbol is called the sheet reference symbol or sheet ref sym for short; the sheet it is referring to is called the child sheet. The sheet ref sym may have zero or more terminals and the child sheet must have the same number of terminals placed directly on the sheet. (In practice these terminals are typically placed from the symbol library; they are called misc/shterm_* and they are not really symbols but terminals.)

The abstract model is produced by the compiler by compiling each root sheet of the current project. Root sheets are all sharing the common namespace at the hierarchy root. When compiling a sheet, the sheet ref sym is detected and the compiler recurses into compiling the child sheet into a new level of hierarchy. The terminals of the sheet ref sym are connected to the terminals of the child sheet.

Child sheets can contain sheet ref syms to further child sheets, making up a multi-level hierarchy. The only restriction is that the hierarchy needs to be free of loops (circular references).

Scopes, addressing

Basic: using only the project global scope

In the simplest setup each child sheet is used only once. There are two ways to make connections across sheets:

The same child sheet can not be referred twice because it would create the same components and networks again.

This is a typical setup for the so called "CPU board" case, where a top level (root) sheet acts as a block diagram connecting terminals of sheet ref syms of a single level of sheets, each realizing a different section of the design, as shown in this example:

10_cpu example circuit

A large CPU is not specified in a single symbol but is split up into a set of different "slot" symbols by functional blocks (this is called heterogeneous slotting). At the end all these slot symbols are merged into the same final abstract component referencing the whole CPU, because they all have the same name (U1). Global nets, such as gnd and Vcc are easy to address simply by referencing them by name - this happens in the stock gnd and Vcc rail symbols.

The sheet reference symbol is manually crafted, one symbol for each child sheet. Such symbols can be constructed right on the root sheet using the graphical editor, or from text files using the utility boxsym-rnd. The sheet ref sym has an attribute called cschem/child/name which holds the name of the child sheet (with or without the .rs ending). The top sheet must be added to the project file as a root sheet and all child sheets must be added to the project file as an aux sheet. The project file needs to be loaded when running sch-rnd. This ensures all sheets are available and can be found by name and the project is self-contained (if the directory is packed up and shared, it will load on anyone else's system as well).

The extra functionality that this setup provides over the plain flat multisheet setup is that the same child sheets can be used by a different project in a slightly different way by doing the wiring between sheet ref syms on the project-specific top (root) sheet differently.

Intermediate: sheet-local objects

Hierarchy can also be used to produce repetitive parts. In this (oversimplified) 20_led example circuit the root sheet contains a microcontroller that is controlling two LEDs using PWM. The "power electronics" of driving the LED is the same on both channels so it makes sense to draw it only once and reference it twice from the root sheet by simply placing the sheet ref symbol twice, using different name for each instance.

To avoid the two instances of the child sheet creating the same components and nets twice, there is a mechanism for creating objects in the sheet local scope. This is done by prefixing the component or net name by ./ (a dot and a slash). This prefix is removed by the compiler and the name is transformed into something unique - by default this is done prepending the hierarchic path, which is a slash separated list of sheet ref sym names from root to the given object. This is how Q1 becomes two FETs: S1/Q1 and S2/Q1. This system works side by side with the basic mechanism from the previous chapter:

So all in all, the intermediate approach uses two different addressing methods: the ./ name prefix to keep something sheet-local; and names with no prefix, which are called automatic, which simply end up being global. (At least in the intermediate use case).

Intermediate: component addressing

In case of the classic "CPU board" example, the intention is that all the U1 symbols are merged together into a single big CPU component. In case of the LED example, the same Q1 on different child sheet instances should be different FETs. This problem is the very same problem as with the networks, and the solution is the same: name prefixing. In fact, components have the same name prefixes and the same behavior as nets.

In the actual examples, this was achieved:

This rule of network and symbol name prefixes work the same is true for the more advanced use cases, for the rest of this document as well, although the document will use networks to demonstrate different tricks.

Intermediate: more on final names

A slightly modified version of the same example is 22_led. This removes the net name pwm1a in the top sheet but the control net is named on the child sheet. In this case the same number of networks are generated and generally the same abstract model is produced, but the network that was called pwm1a in the previous example becomes S2/gate, because that's the second best name sch-rnd could find.

In case the child sheet did not name the control net, a globally unique anon net name would have been generated for this net.

Intermediate: passing down global nets

Another modification of the original LED example is 24_led. This introduces a common enable signal, controlled by the microcontroller, that can cut off both LEDs at once. To avoid having to have an extra terminal on the both sheet ref syms and having to draw the enable signal to both syms, child sheets simply refer to the blink_enable net without the ./ prefix, which means this is a global scope net and all three sheet instances will join its references into a single net.

This is the same mechanism that the GND symbol uses.

Expert: subtree scope

The intermediate setup covers the situation of reusable single-sheet modules, locking internal networks and components into the sheet local scope using the ./ name prefix. In some cases the reusable module is large enough that it needs to be a multi-sheet hierarchy. In that case there should be a single root sheet of the module that references all other sheet of the same module using sheet ref syms. The module is then instantiated by a bigger design by:

(The bigger design is generally not interested in the internals of the multi-sheet module, the interface ("API") between the module and the bigger design should be the terminals of the module's root sheet and a few well documented global nets, such as GND.)

When a common signal needs to be distributed among all subsheets within the module the most trivial solution is to create a new terminal on all the sheet ref syms and do the wiring on the module's root sheet. However this leads back to the same problem of having too many terminals and too many wires on the module's root sheet. It would be easier to use a global net that is accessible from all the module's sheets. However, using a true global net for this purpose is a bad idea, as it may unintentionally merge with the same named global net in the bigger design or with the same named global net of a second instance of the same module that should have been independent.

The solution is using subtree local objects (nets and components). This works by declaring the object (net or component) on the module's root sheet using a v/ prefix to the name. The v/ prefix means this object is mergable only downward in the hierarchy so only subsheets of this module, all somewhere under the module's root sheet will see the object, but it will not merge with anything above the module's subtree.

In example 32_subtree an extension of the 24_led example: the whole led control section with the microcontroller and the two LEDs are packed into a subtree. The root of the subtree is led_ctrl.rs and is almost the same circuit as main.rs in 24_led. The change is that the 2-channel MCU controlled LED subtree is now commanded through SPI.

A new root sheet is added on top, called main.rs, which adds U5, the main microcontroller, then creates two instances of the led control subtree and hooks up U5's SPI to control both led_ctrl microcontrollers (using different slave select signals).

Sheet instance tree will look like:

main.rs (SPI master)
 |
 +- S1: led_ctrl.rs (SPI slave 2 channel LED control)
 |  |
 |  +- S1/S1: led.rs (LED and FET)
 |  |
 |  +- S1/S2: led.rs (LED and FET)
 |
 +- S2: led_ctrl.rs (SPI slave 2 channel LED control)
    |
    +- S2/S1: led.rs (LED and FET)
    |
    +- S2/S2: led.rs (LED and FET)

The expert feature, subtree net, can be observed on sheet led_ctrl.rs. It's the v/blink_enable net. This used to be a global net in example 24_led, so that it didn't need to have a dedicated terminal on the sheet ref syms. In this example we need the same, except that blink_enable can not be fully global because then the two led_ctrl sheets (S1 and S2) would share the same led_ctrl net. What is needed instead is a subtree local network, which means that on the level of S1 and S2 these are two separate networks, but in the subtree under S1 and in the other subtree under S2 the network is accessible without having to use sheet terminals.

The practical implementation is:

Note: on led_ctrl.rs it's possible to name the blink_enable net without any prefix and the example will still work. This is because of the search preference of the auto scope that is used when the name is not prefixed: it will first search sheet local nets to bind to then starts ascending level by level looking for a subtree local net, bumping into the same v/blink_enable on led_ctrl.rs. However, if the search fails (e.g. led_ctrl.rs fails to offer a subtree local net named blink_enable), the auto will silently create a global net whereas the ^/ prefixed name will throw an error, which is a safer choice.

Expert: scope summary and explicit global scoping

A full list of hierarchic name prefixes (and their scopes) can be found in the design document.

The system is designed so that:

  • the auto scope, when no prefix is prepended to the net name, usually does what's expected, searching from local to global, then if no existing net of the same name is found, creating a new global net. This is the only addressing used in the flat (non-hierarchic) case and the most commonly used addressing for non-local objects in the hierarchic case
  • it offers an easy to use ./ prefix, which is the most commonly used local scope prefix in the hierarchic case, locking the given object into the current sheet
  • offers a less often used subtree local mechanism for real complicated case, prefixed with v/ for "search downward" or ^/ for "search upward"
  • offers the exotic / prefix for explicitly addressing the global scope.

    The explicit global scope prefix can be used in the real exotic case when a (near) bottom child sheet needs to address a project global net not being sure there was no same named sheet local or subtree local net in the hierarchy. The auto scope (no prefix) or the ./ or ^/ prefixes would pick up those intermediate nets, may they exist, not reaching the global scope. The / prefix turns off the search mechanism and searches right in the project global namespace; if the object is not found, it is created in the project global namespace.

    Expert: hlibrary

    The simpler usage discussed above, is also the self-contained usage: when all sheets files of the hierarchy are placed in a single project directory and all sheets are listed in the project file (as root and aux sheets). A variation for large projects is placing all sheet files in a directory tree and have a project file in the top directory, listing all sheets.

    This setup is called self-contained because packing up the project starting from its root directory results in a portable file that can be distributed. After unpacking on a different system, all files are available and the project can be compiled. This is because all symbols and devmaps are automatically embedded in the sheet files, all project-specific configuration embedded in the project file.

    Another use case of hierarchic pages is maintaining a library of child sheets; for example in IC design it is common to have symbols for blocks like a XOR gate and then have the "few FET implementation" of that XOR gate as a child sheet in a library. The advantage is that different projects can share and reuse the same child sheets. The drawback is that these child sheets are not copied into the project directory and the project is not self-contained: the library of child sheets need to be distributed alongside the project.

    This option is supported by sch-rnd via path based child sheet references. Reusable child sheets are placed in the so called hlibrary. This is very similar to the symbol library, with the search paths configured in the config node rc/hlibrary_search_paths. Each hlibrary path is a tree of sheet files and directories on the file system. Child sheets from sheet ref syms are addressed using the same cschem/child/name attribute. During project compilation when the given sheet is not found by name in the project file, sch-rnd announces this in the message log and automatically searches the hlibrary path and picks the first file with a matching name. The name is either in the form of "foo.rs" or just "foo" (sch-rnd automatically matches the .rs or .lht endings).

    Example project: 50_hlibrary is a modified copy if the 20_led example; led.rs is the same child sheet, referenced as cschem/child/name=led from main.rs S1 and S2. The file lives in ./hlibrary/, which is a preconfigured standard hlibrary search path. The file is searched and picked up upon the first compilation.

    Sheets loaded this way are not added to the project file and are listed as [E] (for external) sheets in the sheet selector. This is a clear indication that the sheet is not listed in the project file and is automatically loaded from the hlibrary for a hierarchic design.

    The project properties dialog lists external sheets in a separate group and the toggle button converts an external sheet into an aux sheet, adding it to the project file sheet listing with its relative path to the project file. This is useful only if the external sheet is coming from a project-local hlibrary.

    Expert: addressing sheets by path

    Another way of addressing external child sheets that are not listed in the project file is path based addressing. This is done by using the sheet ref sym attribute cschem/child/path instead of cschem/child/name. The path must be relative to the parent sheet's path on the file system. The sheet is automatically loaded as an external sheet during compilation.

    Example project: 52_path is a modified copy if the 20_led example; led.rs is the same child sheet, referenced as cschem/child/path=my_lib/led.rs from main.rs S1 and S2. The file lives in ./my_lib/, which is not on hlibrary search path, thus the file is not searched. It is picked up by a direct file system lookup, using path ./my_lib/led.rs relative to main.rs. Note: since there is no search, there is also no file name matching, thus in the path addressing method the exact file name must be specified, the .rs ending can not be left off.

    This method can not be used to address central hlibrary child sheets because of the relative addressing in the file system. It is useful for large projects with a local system of child sheets with redundant names, sorted into subdirectories. In that case the designer may wish to explicitly refer to the child sheet by relative path instead of by short name depending on a library search algorithm that may pick the wrong file.

    Expert: addressing sheets by uuid

    Not yet supported. (TODO)

    Expert: non-graphical sheets

    Non-graphical sheets define object directly in the abstract model, they do not represent concrete model sheets. Hierarchic mechanisms (like connecting child sheet terminals to sheet ref sym terminals) depend on working with concrete model sheets. Thus non-graphical sheets can not participate in the hierarchy at the moment but can be used as root sheets in global scope.

    Expert: spice unslotting

    A special use case for hierarchic designs is "unslotting" a symbol that was drawn as monolithic symbol. E.g. a dual opamp drawn as a single symbol can be split up into two separate opamps This use case is described in the spice export plugins documentation.