SVG based integrated circuit schematics that seamlessly import and run as Hdl21's Python circuit generators.
Jump to Installation | Development
Schematics are graphical representations of electronic circuits. In integrated circuit (IC, silicon, chip) design they the lingua franca for analog circuits, and also commonly used for transistor-level digital circuits.
In short: a schematic is two things -
Scalable Vector Graphics (SVG) is the W3C's open web-standard for two dimensional vector graphics. SVG is an XML-based markup language which all modern browsers natively render, and includes the capacity for semi-custom content structure and metadata.
Hdl21 schematics are not like SVGs.
They are not exportable to or convertable to SVGs.
They are SVGs.
So:
This inverter is a valid schematic:
And the same inverter with OpenMoji's mind-blown emoji is also a valid schematic:
This is the first, probably biggest, difference between Hdl21 schematics and any other you've likely encountered. Instead of defining a custom schematic format and needing custom software to read it, Hdl21 schematics are general-purpose images. Any remotely modern web browser or OS can read them.
Embedding in SVG also allows for rich, arbitrary annotations and metadata, such as:
SVG is an XML-based schema and allows for semi-custom strucutre and metadata. This structure and metadata, detailed later in this document, is what makes an SVG a schematic.
While reading schematics just requires any old computer, writing them is best done with custom software. The primary Hdl21 schematics graphical editor runs in three primary contexts:
SVG schematics by convention have a sub-file-extension of .sch.svg
. The editor application and VsCode Extension use this convention to identify schematics and automatically launch in schematic-edit mode.
Schematics consist of:
The element-library holds similar content to that of SPICE: transistors, resistors, capacitors, voltage sources, and the like. It is designed in concert with Hdl21's primitive element library.
The complete element library:
Symbols are technology agnostic. They do not correspond to a particular device from a particular PDK. Nor to a particular device-name in an eventual netlist. Symbols solely dictate:
Each instance includes two string-valued fields: name
and of
.
The name
string sets the instance name. This is a per-instance unique identifier, directly analogous to those in Verilog, SPICE, Virtuoso, and most other hardware description formats. It must be of nonzero length, and for successful Python import it must be a valid Python-language identifier.
The of
string determines the type of device. In Python, the of
field is executed as code. It will often contain parameter values and expressions thereof.
Examples of valid of
-strings for the NMOS symbol:
# In the code prelude:
from hdl21.prefix import µ, n
from hdl21.primitives import Nmos
# Of-string:
Nmos(w=1*µ, l=20*n)
# In the code prelude:
from asap7 import nmos as my_asap7_nmos
# Of-string:
my_asap7_nmos(l=7e-9, w=1e-6)
This is probably the second biggest difference between Hdl21 schematics and most other schematic systems. There is no backing "database", no "links" to out-of-source libraries. The types of all devices are dictated by code-strings, interpreted by programs ingesting the schematic.
For a schematic to produce a valid Hdl21 generator, the result of evaluating each instance's of
field must be:
Instantiable
, andHdl21 is a high-productivity analog hardware description library (HDL) embedded in Python. Hdl21's Generator
s are Python functions which produce circuit Module
s. Hdl21 schematics are designed to seamlessly import into Hdl21-based Python programs, as a kind of "graphical Python module".
The inverter pictured above (with or without the emoji) roughly translates to the following Python code:
# A code-prelude, covered shortly, executes here.
@h.generator
def inverter(params: Params) -> h.Module:
inverter = h.Module()
inverter.n0 = Nmos(params)(...)
inverter.p0 = Pmos(params)(...)
return inverter
# Both "..."s are where connections, not covered yet, will go.
Each schematic includes a code prelude: a text section which precedes the schematic content. Typically this code-block imports anything the schematic is to use. The prelude is stored in text form as a (non-rendered) SVG element.
An example prelude:
# An example code-prelude
from hdl21.primitives import Nmos, Pmos
This minimal prelude imports the Nmos
and Pmos
devices from the Hdl21 primitive-element library.
Schematic code-preludes are executed as Python code. All of the language's semantics are available, and any module-imports available in the executing environment are available.
The call signature for an Hdl21 generator function is def <name>(params: Params) -> h.Module:
. To link their code-sections and picture-sections together, Hdl21 schematics require special treatment for each of this signature's identifiers: name
, params
, Params
, and h
.
Params
with a capital P
.
Params
is not defined in the code prelude, the generator will default to having no parameters.Params
must be an Hdl21 paramclass
, or will generate an import-time TypeError
.params
with a lower-case p
.name
attribute is defined in the code-prelude, the generator function's name is set to this string.
name
which is not a string or is not a valid Python identifier will generate an import-time Exception
.h
must refer to the Hdl21 pacakge.
import hdl21 as h
before the schematic's own code-prelude.h
identifier will produce an import-time Python error.hdl21 as h
is fine, as is importing hdl21
by any additional names.An example code-prelude with a custom Params
type:
# An example code-prelude, using devices from PDK-package `mypdk`
import hdl21 as h
from mypdk import Nmos, Pmos
@h.paramclass
class Params:
w = h.Param(dtype=int, desc="Width")
l = h.Param(dtype=int, desc="Length")
Hdl21 schematics are designed to seamlessly integrate into Python programs using Hdl21. They are essentially "graphical Python modules". The hdl21schematicimporter
Python package makes this as simple as:
import hdl21schematicimporter
# Given a schematic file `schematic.sch.svg`, this "just works":
from . import schematic # <= This is the schematic
Schematics with .sch.svg
extensions can be import
ed like any other Python module. The hdl21schematicimporter
package uses Python's importlib override machinery to load their content.
An example use-case, given a schematic named inverter.sch.svg
:
# Example of using a schematic
import hdl21 as h
from .inverter import inverter # <= This is the schematic
@h.module
class Ring:
a, b, c, VDD, VSS = h.Signals(5)
ia = inverter()(inp=a, out=b, VDD=VDD, VSS=VSS)
ib = inverter()(inp=b, out=c, VDD=VDD, VSS=VSS)
ic = inverter()(inp=c, out=a, VDD=VDD, VSS=VSS)
For schematic files with extensions other than .sch.svg
, or those outside the Python source tree, or if (for whatever reason) the import
-override method seems too spooky, hdl21schematicimporter.import_schematic()
performs the same activity, with a filesystem Path
to the schematic as its sole argument:
def import_schematic(path: Path) -> SimpleNamespace
Both import_schematic
and the import
keyword override return a standard-library SimpleNamespace
representing the "schematic module". A central attribute of this module is the generator function, which often has the same name as the schematic file. The Params
type and all other identifiers defined or imported by the schematic's code-prelude are also available as attributes in this namespace.
The experienced schematic-author may by now be wondering: how does one make hierarchical Hdl21 schematics, with custom subcircuit cells and symbols?
The answer is simple: you don't.
Write HDL code instead.
Reading SVG schematics requires no special software or installation: if your web browser can display SVG (and it can), you can read schematics. Double-click on them or open them with the OS-native method of your choice. Or borrow grandma's copy of Internet Explorer and open it with that. Voila.
Editing schematics can, in principle, be done with general-purpose SVG editing software. InkScape and Boxy SVG are popular examples. Or even with a general-purpose text editor. This requires diligently sticking to the schematic schema described below. Many an SVG editor will make this very hard to do, particularly with respect to element hierarchy and grouping. The dedicated schematic editor is highly recommended instead.
The Python schematic-importer package is named hdl21schematicimporter
. It is distributed via PyPi at pypi.org/project/hdl21schematicimporter. To install the Python importer:
pip install hdl21schematicimporter
To install the Python importer from source, see the development quickstart.
The schematic editor uses a popular web-technologies stack. It is written in TypeScript and its peripheral components use the React UI framework. The desktop app uses the cross-platform Electron framework. All of this is very popular, very well-supported, and very easy to get started with.
The schematic editor has a sole dependency: the JavaScript package manager Yarn.
npm
and node
through apt
: sudo apt install nodejs npm
node
through npm
: sudo npm install -g n && sudo n stable
cd Hdl21Schematics/Hdl21SchematicEditor/packages/EditorApp
yarn
to install dependenciesyarn make
to buildThis will produce a platform-specific app in the out
directory.
cd Hdl21SchematicEditor/packages/VsCodePlugin/
yarn
to install dependenciesyarn package
to buildcode --install-extension hdl21-schematics-vscode-0.0.1.vsix
to installFor development-mode installations see the development quickstart.
SVG schematics are commonly interpreted by two categories of programs:
This section serves as the specification for (2). The schema which dictates the content of schematic circuits is dictated through SVG structure and element attributes. While some of this schema also dictates how schematics appear as pictures, overlap between the two use-cases is incomplete. Valid schematic importers must adhere to the schema defined herein and no more.
Note the graphical schematic editor is a special case which combines both use-cases. It simultaneously renders schematics as pictures while being drawn and dictates their content as circuits. The graphical editor holds a number of additional pieces of non-schema information about schematics and how they are intended to be rendered as pictures, including their style attributes, design of the element symbols, and locations of text annotations. This information is not part of the schematic schema. Any valid SVG value for these attributes is to be treated as valid by schematic importers.
Schematic
Each Schematic
is represented by an SVG element beginning with <svg>
and ending with </svg>
, commonly stored in a file with the .sch.svg
extension.
Many popular SVG renderers expect ?xml
prelude definitions and xmlns
(XML namespace) attributes to properly render SVG. SVG schematics therefore begin and end with:
<?xml version="1.0" encoding="utf-8"?>
<svg width="1600" height="800" xmlns="http://www.w3.org/2000/svg">
<!-- Content -->
</svg>
These XML preludes are not part of the schematic schema, but are included by the graphical editor.
Schematics are always rectangular. Each schematic's size is dictated by its svg
element's width
and height
attributes. If either the width or height are not provided or invalid, the schematic shall be interpreted as having the default size of 1600x800.
SVG schematics allow for inclusion of arbitrary non-schematic SVG elements. These might include annotations describing design intent, links to related documents, logos and other graphical documentation, or any other vector graphics content.
These elements are not part of the schematic content. Circuit importers must (a) categorize each element as being either schematic or not, and (b) ignore all elements which are non-schematic content.
SVG schematics include a number of header elements which aid in their rendering as pictures. These elements are not part of the schematic schema, and are to be ignored by schematic importers. They include:
<defs>
) element with the id hdl21-schematic-defs
<style>
) with the id hdl21-schematic-style
<rect
>), of the same size as the root SVG element, with the id hdl21-schematic-background
. This element supplies the background grid and color.SVG schematics use the SVG and web standards for their coordinate system. The origin is at the top-left corner of the schematic, with the x-axis increasing to the right and the y-axis increasing downward.
All schematic coordinates are stored in SVG pixel values. Schematics elements are placed on a coarse grid of 10x10 pixels. All locations of each element within a schematic must be placed on this grid. Any element placed off-grid shall be interpreted as a SchematicError
.
All schematic elements operate on a "Manhattan style" orthogonal grid. Orient-able elements such as Instance
s and Port
s are similarly allowed rotation solely in 90 degree increments. Such elements may thus be oriented in a total of eight distinct orientations: four 90 degree rotations, with an optional vertical reflection. Reflection and rotation of these elements are both applied about their origin locations. Note rotation and reflection are not commutative. If both a reflection and a nonzero rotation are applied to an element, the reflection is applied first.
These orientations are translated to and from SVG transform
attributes. SVG schematics use the matrix
transform to capture the combination of orientation and location. SVG matrix
transforms are specified in six values defining a 3x3 matrix. Transforming by matrix(a,b,c,d,e,f)
is equivalent to multiplying a vector (x, y, 1)
by the matrix:
a c e
b d f
0 0 1
Note that this is also equivalent to a multiplication and addition of the vector two-dimensional vector (x,y)
:
| a c | | x | + | e |
| b d | | y | | f |
In the schematic Manhattan coordinate system, the vector-location (e,f)
may be any grid-valid point. The 2x2 matrix (a,b,c,d)
, however, is highly constrained, to eight possible values which correspond to the eight possible orientations. These eight values are:
a | b | c | d | Rotation | Reflection |
---|---|---|---|---|---|
1 | 0 | 0 | 1 | 0° | No |
0 | 1 | -1 | 0 | 90° | No |
-1 | 0 | 0 | -1 | 180° | No |
0 | -1 | 1 | 0 | 270° | No |
1 | 0 | 0 | -1 | 0° | Yes |
0 | 1 | 1 | 0 | 90° | Yes |
-1 | 0 | 0 | 1 | 180° | Yes |
0 | -1 | -1 | 0 | 270° | Yes |
Any schematic element with an SVG matrix
with (a,b,c,d)
values from outside this set shall generate a SchematicError
.
Each Schematic
is comprised of collections of four types of elements:
Instance
s of circuit elementsWire
s connecting themPort
annotationsDot
s indicating located connectionsThese collections are not ordered or keyed. No element refers to any other by any means, e.g. name, ID, or other "pointer".
Instance
Each Instance
includes:
name
of
, which dictates the type of element to be instantiatedkind
value from the enumerated Elements
list, which serves as pointer to the Element
dictating its pictorial symbol and port list.location
dictating the position of its origin in schematic coordinates.orientation
dictating its reflection and rotation.In SVG, each instance is represented by a group (<g>
) element. Instance groups are identified by their use of the hdl21-instance
SVG class. The location and orientation of each instance is stored in its instance-group's transform
attribute.
Each instance-group holds three ordered child elements:
<g>
) holding the instance's pictorial symbol.
class
of this symbol-group serves as indication of the kind
of the instance.class
attribute indicate the kind
of the instance.<text>
element with class hdl21-instance-name
holding the instance's name.<text>
element with class hdl21-instance-of
holding the instance's of
string.An example Instance
:
<g class="hdl21-instance" transform="matrix(1 0 0 1 X Y)">
<g class="hdl21-elements-nmos">
<!-- Content of the symbol-picture -->
</g>
<text x="10" y="0" class="hdl21-instance-name">inst_name</text>
<text x="10" y="80" class="hdl21-instance-of">inst_of</text>
</g>
The three child elements are required to be stored in the order (symbol, name, of). The lack of valid values for any of the three child elements shall generate a SchematicError
. The presence of any additional children shall also generate a SchematicError
.
SVG schematics instantiate circuit elements from a library of pre-defined symbols. A schematic importer must be aware of this libray's contents, as it dictates much of the schematic's connectivity.
The kind
field of each Instance
serves as a reference to a Element
type. Each Element
consists of:
An example Element
, defined in JavaScript syntax:
Element({
kind: ElementKind.Nmos, // The enumerated `kind`
ports: [
// Its ordered, located port list
new Port({ name: "d", loc: point(0, 0) }),
new Port({ name: "g", loc: point(70, 40) }),
new Port({ name: "s", loc: point(0, 80) }),
new Port({ name: "b", loc: point(-20, 40) }),
],
});
Notably each element does not dictate what device appears in an ultimate circuit or netlist. The of
string of each Instance
dictates these choices. The element solely dictates its two fields: the pictorial symbol and the port list.
The complete list of elements is defined in the circuit element library documentation. The content of the element library - particularly the kinds of elements and their port lists - is part of the schematic schema, and must be adhered to by any schematic importer.
Wire
Schematic wires consist of orthogonal Manhattan paths. They are represented by SVG group (<g>
) elements, principally including an internal <path>
element. Wire groups are indicated by their use of the hdl21-wire
SVG class. Each wire group has two child elements:
path
element dictating the wire's shape.text
element dictating its wire/ net name.An example Wire
:
<g class="hdl21-wire">
<path class="hdl21-wire" d="M 100 150 L 100 350 L 200 350" />
<text class="hdl21-wire-name">net1</text>
</g>
Wire vertices are dictated by the SVG path
's d
attributes. Each wire vertex must be located on the schematic's 10x10 pixel grid. Each wire segment must meet "Manhattan" orthogonal routing style, i.e. each point must have either an x or y coordinate equal to that of the previous point. Wire paths are open in the SVG sense; there is no implicit segment from the final point back to the first.
Wire-names serve as the mechanism for schematic "connections by name". Any two wires with the same name shall be considered connected. There is one special wire-name
value: the empty string, which implies that (a) the wire's is not explicitly set, and (b) importers shall assign it a net-name consistent with any other connected element, e.g. a Port
or another Wire
.
Port
Schematic Port
s appear similar to Instance
s in both pictorial representation and in SVG content. Unlike instances they do not add hardware to the circuit represented by the schematic, but annotate particular Wire
s as being exposed externally.
Each Port
has the following fields:
name
kind
value from the enumerated PortKind
listlocation
dictating the position of its origin in schematic coordinates.orientation
dictating its reflection and rotation.Note these fields are identical to those of Instance
, but for the removal of the of
string-field. The semantic content of a schematic Port
is dicated fully by its Kind
field, which also dictates its pictorial representation.
In SVG, each Port
is represented by a group (<g>
) element. Port groups are identified by their use of the hdl21-port
SVG class. The location and orientation of each instance is stored in its port-group's transform
attribute.
Each port-group holds two ordered child elements:
<g>
) holding the port's pictorial symbol.
class
of this symbol-group serves as indication of the kind
of the port.class
attribute indicate the kind
of the port.<text>
element with class hdl21-port-name
holding the port's name.An example Port
:
<g class="hdl21-port" transform="matrix(1 0 0 1 X Y)">
<g class="hdl21-ports-input">
<!-- Content of the symbol -->
</g>
<text x="10" y="-15" class="hdl21-port-name">portname</text>
</g>
Valid port names must be non-zero length. All wires connected to a port shall be assigned a net-name equal to the port's name. Any such connected wire with a conflicting net-name shall generate a SchematicError
. Any wire or connected combination of wires which are connected to more than on port shall generate a SchematicError
.
Dot
Schematic dots indicate connectivity between wires and ports where connections might otherwise be ambiguous. The inclusion of a Dot
at any location in a schematic implies that all Wire
s passing through that point are connected. The lack of a Dot
at an intersection between wires conversely implies that the two are not connected, and instead "fly" over one another.
Dot
s are represented in SVG by <circle>
elements centered at the dot location. Dot locations must land on the 10x10 pixel schematic grid. Dot-circles are identified by their use of the hdl21-dot
SVG class.
An example Dot
:
<circle cx="-20" cy="40" class="hdl21-dot" />
The center location dictating cx
and cy
attributes are the sole schema-relevant attributes of a dot-circle. All other attributes such as the radius r
are not part of the schema, and may be any valid SVG value.
While powerful visual aids and a notional part of the schematic-schema, Dot
s do not have semantic meaning in schematics. They are entirely a visual aid. Schematic importers shall not imbue them with meaning. I.e. any valid schematic with any combination of Dot
s yields an identical circuit with any other combination of Dot
s.
The primary editor application infers Dot
s at load time, and uses those stored in SVG as a check. This process includes:
Differences between the inferred and stored dot locations are logged and reported. They shall not generate a SchematicError
.
Note: SVG includes a definitions (<defs>
) section, which in principle can serve as a place to hold the element symbol definitions. Doing so would save space in the hypertext content. But we have very quickly found that popular platforms we'd like to have render schematics (ahem, GitHub) do not support the <defs>
and corresponsing <use>
elements.
The Hdl21 schematic system largely breaks into two interdependent pieces of software:
One notable difference between the two: their programming language. The editor uses a TypeScript-based web-stack, and the importer is pure Python. The two require different toolchains (yarn
and pip
), both of which are highly accessible and easy to install.
To debug the desktop application:
cd Hdl21Schematics/Hdl21SchematicEditor/packages/EditorApp
yarn
to install dependenciesyarn start
to start the applicationTo debug the VsCode Extension:
cd Hdl21Schematics/Hdl21SchematicEditor/packages/VsCodePlugin/
yarn
to install dependenciesyarn watch
to build the extension and watch for changesF5
to start the VsCode Extension in debug modeTo dev-install the Python importer:
cd Hdl21SchematicImporter
pip install -e ".[dev]"
to install in dev modepytest
to run the test suiteHdl21SchematicEditor is broken in several components, organized as JavaScript packages:
EditorCore
and its underlying "platforms", i.e. the other packages.