We need a universal translator system that can translate in all directions between Free EDA tools, possible future free EDA tools, and outside tools that are likely to be used with free EDA tools.
Of course, everything to everything is not reasonable. So, set a limit of circuit oriented free EDA tools, possible future tools, and outside tools that are likely to be used them. Of course, tool formats where translation doesn't make sense don't need to be supported.
Most tools use a format much like a netlist, often with some special information. So, use a netlist based intermediate format. First translate to the intermediate format, then translate out. The intermediate format should be sufficiently expressive that there can be a lossless round trip from the tool format to the intermediate format and back.
Lossless means that the resultant file is equivalent in how it works. It is not necessary to preserve formatting and other things that don't matter.
All of the formats needing translation presently consist of lists of objects, with some kind of encapsulation. Each object has connections and attributes.
This suggests the possible of a standard netlist format as the intermediate format.
Further discussion related only to formats that fit this model.
If possible, the format chosen should have a history of use for at least part of this, and have a published specification that is externally controlled and freely available.
There needs to be a way to merge changes from any target/source without messing up other parts.
Lossless round trip is required, so archival storage can use the intermediate format.
Support for these will allow gEDA tools to play nice with the commercial world. Basic functionality is needed, but it doesn't need to be lossless. Lossless should be possible, but it is not a high priority to actually implement it.
Hopefully having a translator system will provide a seed so these can be done.
All of these consist of lists of objects, with connections and attributes.
It is tradition that a netlist is used for interchange, but the traditional approach only goes one way, because information is lost in the translation.
The format must convey the meaning, not necessarily in the same way as the tool's native format or internal storage.
It is not necessary to translate parts that are usually in libraries, and are tool specific, such as models, symbols, or footprints.
All contenders for possible formats must support a lossless round-trip to any other.
A popular netlist format. It has a history of use for interchange, but not yet for physical placement. Problems: irregular syntax, not sufficiently expressive. These problems have been a major hassle for years for developers. It is well accepted, but not by people who know it well.
The structural subset is a good netlist format. It is regular, sufficiently expressive, and has a published standard. It has a history of use for interchange, but not yet for physical placement.
The structural subset is a good netlist format. It is regular, sufficiently expressive, and has a published standard. It has a history of use for interchange, but not yet for physical placement.
The structural subset is a good netlist format. It is regular, sufficiently expressive, but belongs to one company (Cadence), so rule it out. It has a history of use for simulation only.
XML is not really a format but a syntax. A good format can easily be made based on XML, but has no history of use in a similar context. The syntax is well documented but there is no outside documentation of application in any related use.
This part is the only part where there is not a strong history of use for VHDL and Verilog.
Ideas:
Choosing the Verilog format as one possibility.
The unit of encapsulation is the “module”:
module my-module(connections); // contents endmodule
Each object in the list has a consistent syntax:
type #(parameters) name (connections);
Example:
Here is a simple circuit, plain Verilog, no placement yet.
module amp (a, c); resistor #(.r(1k)) r1 (.p(a), .n(b)); resistor #(.r(1k)) r2 (.p(b), .n(c)); opamp741 #(.gain(100k)) u1 (.p(c), .n(0), .ps(0), .pn(b)); endmodule
Looking at it one line at a time:
module amp (a, c);
A “module” is the unit of encapsulation. This one is called “amp”, and it has two connections to outside, “a” and “c”.
resistor #(.r(1k)) r1 (.p(a), .n(b));
This line instantiates a “resistor”. It could be a symbol, a device, a footprint, depending on the application.
The ”#” introduces a parameter list. “r” is the name of a parameter. “1k” is the value (a string).
“r1” is the instance name,
It has two pins, named “p” and “n'. Node “b” connects to pin “p” and node “c” connects to pin “n”.
The type (“resistor”) refers to a model in simulation. In layout or schematic, it would refer to a footprint, indirectly.
In simulation, the models often come from some place external, or might come with the simulator. You might substitute different models for the same device, depending what you want to do.
In layout or schematic, the footprints and symbols are not conveyed by this file, and might be changed in a transfer, like you would change a font in a text document. This provides a mechanism for making changes like from through-hole to surface mount parts.
In the above example, both connect to node b directly. In a schematic or layout representation the connection would not be direct, but through a “net”.
module amp (.a(a0), .c(c0)); resistor #(.r(1k)) r1 (.p(a1), .n(b1)); resistor #(.r(1k)) r2 (.p(b2), .n(c2)); opamp741 #(.gain(100k)) u1 (.p(c3), .n(0), .ps(0), .pn(b3)); net a (a0, a1); net b (b1, b2, b3); net c (c0, c2, c3); endmodule
We have replaced the nodes with nets, which are now first class objects. This will give us a way to represent the interconnect in a schematic drawing or a layout. It also provides essential data to support analysis and simulation of the interconnect.
Looking at net “b” as an example, it has 3 connections : r1 pin n, p2 pin p, and u1 pin pn.
What is a “net”? It depends how you look at it. It could be the lines on a schematic, or the traces on a PC board. If we are doing a Spice-type simulation, we would want to collapse it into a single node. So we might define:
module net (.a(z), .b(z), .c(z), .d(z), .e(z), .f(z)); endmodule
This definition gives us up to 6 connections (a,b,c,d,e,f) as seen from outside, all connected together internally, which has the effect of collapsing it all down to one.
The Verilog standard defines some “system” parameters for all devices to show position.
These “system” parameters are real parameters, so we won't use them here. We will use them as inspiration for “attributes” instead.
The Verilog standard gives a way to add “attributes” to just about anything.
(* attribute = value *) resistor r1 (.p(2), .n(3));
We will use a special prefix for the attribute names, so the various uses can coexist. The prefix will be one or two upper case letters, a single digit [0..9], and an underscore. The digit is a selector, allowing several different markups in the same file. This prefix list can be extended for other uses.
Prefix | Purpose |
---|---|
S | Schematic |
PC | Printed Circuit Layout |
L | IC Layout |
We add a third dimension “z”, and a third flip “zflip”. We shorten xposition to x, yposition to y, and add a number to have multiple positions. The “z” may indicate a layer name instead of a dimension, depending on the application.
Now we use the attributes to specify the location. We need to locate the various objects. In most cases, we need to specify some point of every object that will identify its location. Some programs use some notion of a “center”, which is ambiguous. We will use the electrical connection points, often called “pins”, as the location points for things that have an electrical connection. For objects that do not have electrical connections, reference points can be used.
We will number the pins, by position, 1, 2, … Then we use x1,y1, and so on to locate them. Usually one of them is adequate to locate an object. The others can “float”, allowing the actual location to be determined by the surroundings. It is permissible to overspecify locations, provided they are self-consistent, and consistent with connections.
Pin numbering starts at 1 (not 0) to be consistent with most IC and connector pin numbering. Pin names should be used instead of numbers if the pins have names.
In addition to the positions, hflip, vflip, and angle are supported. If there is both a flip and an angle, flip will be done first, then angle. The angle is specified in degrees counterclockwise, but only 0, 90, 180 and 270 are expected to be supported.
Another attribute named for the specific tool (example: S0_geda
) can be used to stash tool specific data that doesn't fit otherwise. This is intended to assist with a translation from this format back to the tool format. Normally, this would be a string containing a composite of the info (S0_geda=“5 10 0 0 0 0 1”
) If more than one string in a scope is needed, suffixes can be used. (S0_geda_color=“blue” S0_geda_symbol=“resistor-1.sym”
) These are stored and passed on without any interpretation. For a simple symbol substitution, you might just do S0_symbol=“input”
so the schematic shows the “input” symbol, overriding “inout”, which is there for the simulator.
module amp ( (* S0_x1=-5m, S0_y1=0m, S0_symbol="input" *) inout electrical .a(a0), // show symbol "input", but actual port direction is inout (* S0_x1=30m, S0_y1=-3m *) output electrical .c(c0) // port syntax per SystemVerilog 3.1a, 18,9 ); // a and c are pin names, user specified, outside. a0 and c0 refer to the node the pin connects to, inside ground g0; ground g1; (* S0_x_p=0m, S0_y_p=0m *) resistor #(.r(1k)) r1 (.p(a1), .n(b1)); // by pin name (* S0_x_p=24m, S0_y_p=7m *) resistor #(.r(1k)) r2 (.p(b2), .n(c2)); (* S0_x1=25m, S0_y1=-3m *) opamp741 #(.gain(100k)) u1 (c3, g1, g0, b3); // by pin number net a (a0, a1); net b (b1, b2, b3); net c (c0, c2, c3); endmodule
The choice of which nodes to locate could have been different. The following example produces exactly the same result.
module amp (.a(a0), .c(c0)); // port syntax per IEEE 1364-2005 input electrical a0; // a0, not a. a0 is inside the module. a is outside. output electrical c0; // see IEEE 1364-2005 12.3.3 ground g0; ground g1; resistor #(.r(1k)) r1 (.p(a1), .n(b1)); resistor #(.r(1k)) r2 (.p(b2), .n(c2)); opamp741 #(.gain(100k)) u1 (.p(c3), .n(g1), .ps(g0), .ns(b3)); (* S0_x1=-5m, S0_y1=0m, S0_x1=0m, S0_y1=0m *) net a (a0, a1); (* S0_x2=24m, S0_y2=7m *) net b (b1, b2, b3); (* S0_x1=30m, S0_y1=-3m, S0_x2=25m, S0_y2=-3m *) net c (c0, c2, c3); endmodule
The following example is over determined, but legal, and produces the same result.
module amp ( (* S0_x1=-5m, S0_y1=0m *) inout electrical .a(a0), (* S0_x1=30m, S0_y1=-3m *) output electrical .c(c0), ); ground g0; ground g1; (* S0_x1=0m, S0_y1=0m *) resistor #(.r(1k)) r1 (.p(a1), .n(b1)); (* S0_x1=24m, S0_y1=7m *) resistor #(.r(1k)) r2 (.p(b2), .n(c2)); (* S0_x1=25m, S0_y1=-3m *) opamp741 #(.gain(100k)) u1 (.p(c3), .n(g1), .ps(g0), .ns(b3)); (* S0_x1=-5m, S0_y1=0m, S0_x2=0m, S0_y2=0m *) net a (a0, a1); (* S0_x2=24m, S0_y2=7m *) net b (b1, b2, b3); (* S0_x1=30m, S0_y1=-3m, S0_x3=25m, S0_y3=-3m *) net c (c0, c2, c3); endmodule
Markups can be combined. In this example, schematic S0_
and printed circuit PC0_
are combined in a single file.
module amp (.a(a0), .c(c0)); input a0; output c0; resistor #(.r(1k)) r1 (.p(a1), .n(b1)); resistor #(.r(1k)) r2 (.p(b2), .n(c2)); opamp741 #(.gain(100k)) u1 (.p(c3), .n(0), .ps(0), .ns(b3)); (* S0_x1=-5m, S0_y1=0m, S0_x2=0m, S0_y2=0m, PC0_x1=-5m, PC0_y1=0m, PC0_x2=0m, PC0_y2=0m *) net a (a0, a1); (* S0_x2=24m, S0_y2=7m *) (* PC0_x2=24m, PC0_y2=7m *) net b (b1, b2, b3); (* S0_x1=30m, S0_y1=-3m, S0_x3=25m, S0_y3=-3m, PC0_x1=30m, PC0_y1=-3m, PC0_x3=25m, PC0_y3=-3m *) net c (c0, c2, c3); endmodule
Portions that apply in only certain contexts can be selectively included with 'ifdef
. This may be useful when the component list needs to be different for the different applications, such as when the nets have different forms for a different route or parameters. Macros like __S0__
and __S0__geda__
are automatically predefined if appropriate. These macros should not be defined in the file, except temporarily for debugging.
module amp (.a(a0), .c(c0)); input a0; output c0; resistor #(.r(1k)) r1 (.p(a1), .n(b1)); resistor #(.r(1k)) r2 (.p(b2), .n(c2)); opamp741 #(.gain(100k)) u1 (.p(c3), .n(0), .ps(0), .ns(b3)); `ifdef __S0__ (* S0_x1=-5m, S0_y1=0m, S0_x2=0m, S0_y2=0m *) net a (a0, a1); (* S0_x2=24m, S0_y2=7m *) net b (b1, b2, b3); (* S0_x1=30m, S0_y1=-3m, S0_x3=25m, S0_y3=-3m *) net c (c0, c2, c3); `elsif __PC0__ (* PC0_x1=-5m, PC0_y1=0m, PC0_x2=0m, PC0_y2=0m *) net a (a0, a1); (* PC0_x2=24m, PC0_y2=7m *) net b (b1, b2, b3); (* PC0_x1=30m, PC0_y1=-3m, PC0_x3=25m, PC0_y3=-3m *) net c (c0, c2, c3); `else net a (a0, a1); net b (b1, b2, b3); net c (c0, c2, c3); `endif endmodule
It is intended that the verilog type (resistor, opamp741, net in this example) and the parameter lists (#(…)
) could be used directly by a simulator or other tool, but also allowing substitution using whatever mechanism the tool provides, which could be module or paramset in Verilog.
For symbols (in schematics) or footprints (layout), ideally this mapping would be resolved automatically globally. Alternatively, in could be resolved locally using an attribute.
(* S0_geda_symbol="resistor2.sym" *) resistor #(10k) r4 (a,b);