sch-rnd - terminal display name

In a nutshell

Sch-rnd doesn't alaways display drawing object (concrete model) attributes directly on screen and export. For many things, like label of terminals, (really: final terminal name) we use display names, which are calculated.

This allows final names and displayed labels to change depending on which view (workflow) is active, e.g. show specific "pin numbers" for a pcb view, but when the user changes the view to spice, show the positional numbers (in the spice file) of the same pins instead.

In the most common case this is all done automatically after setting the devmap attribute of a symbol. But there are two more, lower layers, and the user may intervene there: portmap and direct pin name/number attributes.

The problem

In the cschem data model (that sch-rnd uses), sheet graphics and attributes entered by the user are all part of the concrete model. Before a netlist export or other complex output can be generated, the concrete model needs to be compiled into an abstract model.

The abstract model is the true meaning of the schematics, reflects the intentions of the user. For example a complex microcontroller may be split up into multiple symbols on the concrete model and these symbols may be placed across multiple sheets. At the end, the user knows it's all the same one physical MCU because all symbol had the name "U17". The abstract model simply represents this knowledge because the compiler understands those symbols are all part of the same big component and creates that one U17 component in the abstract model.

Compiling matching symbols into a single component is simple. Having the right terminal names ("pin numbers", in PCB terms) is a bit trickier, because there are factors like:

This chapter explains how sch-rnd solves these problems for terminals.

What is a display name?

A display name is an attribute called display/name in the abstract model. In the abstract model ports of components (which are compiled from terminals of symbols) will typically have this attribute set by the compilation process. There is usually a floater+dyntext object placed next to the terminal in the concrete model, which has ../a.display/name in its template. This prints the display name of the abstract port that was compiled from the concrete terminal.

The anatomy of the display name

The reference ../a.display/name is split in the middle, at the dot.

The left side, "../a" means "fetch an attribute of the abstract parent", because ".." means parent (like in a UNIX file system) and "a" means "attribute of the abstract object". The parent of the text object in this case is the concrete group that represents the terminal; the abstract parent is the abstract port that got compile from this terminal. (The port is an abstract group with type=PORT in the abstract model).

The right side is an attribute name (key), "display/name". On the right side slash does not have any special meaning, it's just a convention to group attributes visually.

What writes display/name?

Compilation takes all concrete objects and generate varying number of abstract objects, using engine plugins. Engines whose name start with "target" are typically responsible for rendering display/name attributes.

How concrete port name or pinnum is compiled to display name in the abstract model by target_pcb

Let's take the problem of a simple diode that will need to have a different pinout for:

To use the same sheet (concrete model) for both PCB and spice, there are two different views set up. One has the "target_pcb" plugin in it, the other has the "target_spice" plugin in it. One of the things these two plugins differ is how they calculate the value of display/name for ports.

Target_spice is the simpler case: it looks at some device type (TODO) attribute of the symbol, which would be diode (TODO), and it would know that for spice it's always "Dname N+ N- modelname", so anode is pin 1 and cathode is pin 2.

Target_pcb looks at the attribute pcb/pinnum, if it exists, display name is set to that. If it doesn't, it looks for a plain pinnum attribute and uses that. If that doesn't exist either it falls back using the name of the terminal.

So at the end, the display pin number will potentially differ depending on which target_* plugin participated in compiling. (Without compiling there is no display name so a dummy is displayed.)

PCB workflow: how to set terminal display name

There are different methods depending on complexity of the symbol.

Heavy symbol

The simplest setup is hardwiring a footprint attribute in the symbol and pin numbers in the terminals. Such symbol will work with only a matching footprint and is called a heavy symbol.

The easiest way to do this is to simply set the name attribute of terminals to the final pin number. Alternatively, if symbolic names (like A and C in case of a diode) are preferreed, set the pcb/pinnum attribute for each terminal. Because of how target_pcb picks the display name for terminals, either will result in the right display name.

The drawback of this method is that symbols are not generic in the physical device/vendor/pinout sense. If there are three different n channel FETs are used in the design, they need three different symbols so they each can hardwire the footprint and the pinout in the symbol. In a symbol library this means many many copies of the same graphics only for the difference of a few text fields. It's also hard to extract the actual pinout from such symbol files both programatically and by looking at the file.

Heavy symbols are not generic in workflow sense either: a heavy symbol designed to work for the pcb workflow, hardwiring smd lead numbers will likely unusable in a breadboard or spice simulation context.

Target plugins

To avoid the per workflow problem, we prefer using terminal names and different "pin number" attributes per workflow. For example the base terminal of a workflow-generic BJT would be called "B" or "base" (this is the name attribute), then pcb pin number would be specified as "1" in the pcb/pinnum attribute and spice/pinnum would be 2 (it's "Qname C B E model" in spice).

Each view normally contains a target_* plugin, which has a workflow-specific heuristics to determine the final pin number. For example target_pcb will first look if there's a pcb/pinnum attribute available and use that; if that fails, it goes for pinnum; if that fails too, it assumes heavy symbol and just copies the original terminal name attribute. Other target_ plugins, e.g. target_spice, will prefer different input attributes so if the terminal has all different workflow specific attributes filled in, every target_ plugin can pick the right one. Or if not, fall back to a more generic attribute.

How concrete port name or pinnum is compiled to display name in the abstract model using different target plugins

The result is always written to the display/name attribute of the component's port in the abstract model. It's displayed on screen because the text object displaying the port label will normally reference the display/name attribute, not the input name attribute.

Since different workflows will simply use different views, and different views will use different target_ plugins, this mechanism guarantees that it is possible to craft generic light symbols that work with any workflow, and it is only a question of adding the right attributes.

For the rest of this document drawings will show only one view and only until the abstract model is finished, to keep drawings simple.

Portmap

However, the above does not solve the problem where the same symbol needs to deliver different pinouts within the same workflow, e.g. depending on the footprint.

The middle level solution cschem model offers is port mapping. It is implemented as the portmap attribute of the symbol. The portmap attribute is an array, each element describes a "terminal_name -> attribute=value" pair. The std_devmap plugin goes and applies each element: it finds the terminal referenced by the left side of the arrow and sets the attribute on it as requested by the right side. Attributes are typically pin numbers, e.g. "B -> pcb/pinnum=1" and "B -> spice/pinnum=2" for the above example.

How portmap is applied to generate final display name of a port

This way there's a central array or table in the symbol that describes the pinout, potentially for all workflows. The same BJT symbol can be placed in two copies and the pcb/pinnum attributes of the portmap attribute modified in one of the copies to get it work with a different physical device.

In other words, it's possible to reuse the graphics and structure of the symbol and deal with the port mapping per case only.

Devmap

The portmap mechanism is real convenient in the small scale, when a given symbol has the right mapping for majority of the cases and occassionally local deviation is needed. However, it does not scale well for the common case of using dozens of different transistors all having the same symbol but potentially different pinout and footprint. Even if there would be only 5..6 different footprint+pinout combinations for the board, it would not be practical to maintain the portmap attribute manually for each instance.

Fortunately it can be automated. The standard way is using devmap, part of the std_devmap plugin. Devmap keeps a library, a "database". Each entry in the devmap library has a name and a list of attributes to set on a symbol.

Using the devmap is simple:

When std_devmap processes the absract model, it will pick up the devmap attribute, look it up in the devmap library and fill in the footprint and portap from there.

How devmap is applied to generate final display name of a port

In a sense on the user interface this method is an alternative to setting the footprint. The cost of setting the devmap attribute instead is not significantly higher, but using a rich devmap entry setting that single attribute can auto-fill-in all device-specific attributes.

Conclusion

For the PCB workflow example, standard options for setting display name (final/output name) of a port:

 symbol attributesterminal attributepros cons
heavy symbol footprint pcb/pinnum
or pinnum or name
simple; all pinout data is in symbol and term attributes every part requires a different symbol
pinmap footprint and pinmap[] name a generic symbol with local attribute modification can be used for any part; all pinout data is in symbol attributes have to look up pinout and set pinmap attribute manually
devmap devmap name a generic symbol with a devmap lib can be used for any part; maintaining the pinout data in a separate devmap lib ("parts lib") may be easier than maintaining them in symbols have to maintain a devmap lib