Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
branch: master
439 lines (372 sloc) 15.053 kb
/**
* Eagle to Spice
*
* This User Language Program will generate a SPICE circuit from an Eagle
* schematic, ready for simulation.
*
* To incorporate SPICE data into your Eagle schematic, parts can be given
* a "SPICE" attribute. The value of this attribute will be used to generate
* the SPICE netlist for that part. It may contain placeholders for any of the
* available substitutions (see below) or any other valid SPICE data.
*
* It is recommended that you add "SPICE" attributes to the Eagle Library
* devices you want to use in simulations. These attributes are inherited by
* instances of that device automatically, and can be overridden in the
* schematic for specific parts that need tweaking. The monowave-eagle
* package (http://github.com/xdissent/monowave-eagle), in which this file
* is included, contains valid SPICE data for most devices in its libraries,
* as well as a few useful devices used specifically for SPICE simulations.
*
* Global SPICE data for a schematic can be added to the "SPICE" global
* attribute. It is recommended that you define things like VCC the supply
* voltage globally. Note that Global SPICE data may only contain attribute
* substitutions, newlines, operations.
*
* Substitutions
* =============
*
* The "SPICE" attribute for an Eagle part may contain placeholders, which
* will be replaced with a value determined at runtime to generate the
* SPICE netlist for that part. This allows you to include part specific data
* in the SPICE netlist when defining the "SPICE" attribute for the Eagle
* device.
*
* Available substitutions:
*
* Placeholder Replacement
* ----------- -----------
* {NAME} The name of the part in the schematic.
* {VALUE} The value of the part in the schematic.
* {<PIN>.NET} The net (SPICE node) to which the pin named <PIN> is
* attached.
* {ATTR.<ATTR>} The value of the attribute named <ATTR>.
* \n A newline character. This is required for multiline SPICE data
* because Eagle does not allow multiline attributes for parts.
*
* Example:
* ATTRIBUTE C1 'SPICE' 'C{NAME} {A.NET} {C.NET} {VALUE}'
*
* Operations
* ==========
*
* "SPICE" attributes may contain simple operations to calculate complex values
* at runtime. Operations may be nested and always return a real value without
* units. Either term in the operation may contain a scale factor and/or units,
* and conversion to a real number will be done before the operation is evaluated.
*
* Available operations:
*
* Operator Operation
* -------- ---------
* LN <RIGHT> Natural logarithm of value.
* LOG <RIGHT> Base 10 logarithm of value.
* <LEFT> / <RIGHT> Division.
* <LEFT> * <RIGHT> Multiplication.
* <LEFT> + <RIGHT> Addition.
* <LEFT> - <RIGHT> Subtraction.
*
* Example:
* ATTRIBUTE R1 'SPICE' 'R{NAME} {1.NET} {2.NET} {{{VALUE} * {{ATTR.POSITION} / 100}} + 0.001}'
*
* SPICE Models
* ============
*
* Including SPICE models in Eagle parts is possible by defining the "SPICEMOD"
* attribute. The value may only contain the "\n" placeholder. Each model
* is only defined once in the final circuit file. (not implemented yet!)
*
* Example:
* ATTRIBUTE D1 'SPICEMOD' '.model 1N4148 D (IS=0.1PA, RS=16 CJO=2PF TT=12N BV=100 IBV=1nA)'
*/
#usage "en: Eagle to Spice\n"
"This User Language Program will generate a SPICE circuit from an Eagle\n"
"schematic, ready for simulation."
/**
* str_replace
*
* Replaces all occurrences of a substring found within a string.
*/
string str_replace(string search, string replace, string subject) {
int lastpos = 0;
while(strstr(subject, search, lastpos) >= 0) {
int pos = strstr(subject, search, lastpos);
string before = strsub(subject, 0, pos);
string after = strsub(subject, pos + strlen(search));
subject = before + replace + after;
lastpos = pos + strlen(replace);
}
return subject;
}
/**
* str_rreplace
*
* Replaces all occurrences of a regex pattern found within a string.
*
* Care has been taken to ensure that the replacement text is never
* tested against the regex to prevent infinite loops.
*/
string str_rreplace(string search, string replace, string subject) {
int lastpos = 0;
while (strxstr(subject, search, lastpos) >= 0) {
int len = 0;
int pos = strxstr(subject, search, lastpos, len);
string before = strsub(subject, 0, pos);
string after = strsub(subject, pos + len);
subject = before + replace + after;
lastpos = pos + strlen(replace);
}
return subject;
}
/**
* str_trim
*
* Removes whitespace from the beginning and end of a string.
* If the scale factor isn't one of the predefined SPICE values,
* 1 will be returned.
*
*/
string str_trim(string subject) {
int pos = 0;
int len = 0;
pos = strxstr(subject, "\\S+", pos, len);
return strsub(subject, pos, len);
}
/**
* scale_to_multiplier
*
* Returns the multiplier for the given scale.
*
* The scale will be trimmed of whitespace before processing.
*
* Example: "K" => 1000
*
*/
real scale_to_multiplier(string scale) {
scale = strupr(str_trim(scale));
if (scale == "T") {
return pow(10, 12);
} else if (scale == "G") {
return pow(10, 9);
} else if (scale == "MEG") {
return pow(10, 6);
} else if (scale == "K") {
return pow(10, 3);
} else if (scale == "MIL") {
return 2.54 * pow(10, -6);
} else if (scale == "M") {
return pow(10, -3);
} else if (scale == "U") {
return pow(10, -6);
} else if (scale == "N") {
return pow(10, -9);
} else if (scale == "P") {
return pow(10, -12);
} else if (scale == "F") {
return pow(10, -15);
}
return 1;
}
/**
* str_to_units
*
* Converts a string to a real value taking into account the units supplied.
*
* If the subject supplied has non-numeric characters at the beginning, they are
* removed. If the value cannot be parsed, 0.0 will be returned. If units are
* supplied, they will also be removed.
*
* Example: "100k" => 1000, "2.2uF" => 0.0000022
*/
real str_to_units(string subject) {
real value = 0.0;
int len = 0;
// Find first integer or real value.
int pos = strxstr(subject, "[\\d\\.]*", 0, len);
if (pos >= 0) {
// Extract found integer or real and convert to real.
value = strtod(strsub(subject, pos, len));
// Find first scale factor if any.
pos = strxstr(strupr(subject), "(MIL|MEG|T|G|K|M|U|N|P|F)", pos, len);
if (pos >= 0) {
// Multiply the value by the scale factor.
value = value * scale_to_multiplier(strsub(subject, pos, len));
}
}
return value;
}
/**
* math_replace
*
* Replaces all math operations with the calculated value.
*
* This function currently evaluates the following operations:
* / Division
* * Multiplication
* + Addition
* - Subtraction
* LEN Logarithm
* LOG Natural logarithm
* EXP Raise the left term to the power of the right.
*
* Example: {100k * {5 * 2.2k}}
*/
string math_replace(string subject) {
// Regex to find math operators in a substitution.
string operator_re = "((\\s+[*/+\\-])|log|LOG|ln|LN|exp|EXP)\\s+";
// Regex to find math substitutions.
string substitution_re = "\\{[^{}]*" + operator_re + "[^{}]+\\}";
// Loop through all possible math substitutions.
while (strxstr(subject, substitution_re, 0) >= 0) {
// The length of the substitution.
int len = 0;
// Find the position of the substitution.
int pos = strxstr(subject, substitution_re, 0, len);
// Save the text before and after this substitution.
string before = strsub(subject, 0, pos);
string after = strsub(subject, pos + len);
// Get the substitution.
string substitution = strsub(subject, pos, len);
// Find the operator.
int operator_len = 0;
int operator_pos = strxstr(substitution, operator_re, 0, operator_len);
// The left term (if it exists);
real left = str_to_units(str_trim(strsub(substitution, 1, operator_pos)));
// The operator.
string operator = strupr(str_trim(strsub(substitution, operator_pos, operator_len)));
// The right term.
real right = str_to_units(str_trim(strsub(substitution, operator_pos + operator_len - 1)));
// The calculated value.
real value = 0;
// Do the math.
if (operator == "+") {
value = left + right;
} else if(operator == "-") {
value = left - right;
} else if(operator == "*") {
value = left * right;
} else if(operator == "/") {
value = left / right;
} else if(operator == "LOG") {
value = log10(right);
} else if(operator == "LN") {
value = log(right);
} else if(operator == "EXP") {
value = pow(left, right);
}
// make the substitution
sprintf(subject, "%s%f%s", before, value, after);
}
return subject;
}
/**
* main
*
* Generates the SPICE circuit.
*/
int main() {
// Return value.
int ret = 0;
// Only valid in schematic mode.
if (schematic) {
// Use the entire schematic.
schematic(SCHEM) {
// Determine the filename.
string outfile = filesetext(SCHEM.name, ".cir");
// Create a models array.
string models[];
int m = 0;
// Create a netlist array.
string netlist[];
int n = 0;
// Iterate over each part.
SCHEM.parts(P) {
// Check to see if it has SPICE models defined.
if (P.attribute["SPICEMOD"]) {
// The SPICE data to output.
string out = P.attribute["SPICEMOD"];
// Handle newlines.
out = str_replace("\\n", "\n", out);
models[m++] = out;
}
// Check to see if it has SPICE data defined.
if (P.attribute["SPICE"]) {
// The SPICE data to output.
string out = P.attribute["SPICE"];
// Substitute the name for "{NAME}".
out = str_replace("{NAME}", P.name, out);
// Substitute the value for "{VALUE}".
out = str_replace("{VALUE}", P.value, out);
// Iterate over each part instance.
P.instances(I) {
// Iterate over each instance's pins.
I.gate.symbol.pins(PIN) {
// Substitute the pin net name for "{P.NET}" where "P" is the pin name.
out = str_replace("{" + PIN.name + ".NET}", PIN.net, out);
// Substitute the pin net name for "{G.P.NET}" where "G" is the gate name.
out = str_replace("{" + I.gate.name + "." + PIN.name + ".NET}", PIN.net, out);
}
}
// Iterate over each part attribute.
P.attributes(A) {
// Substitute value of attribute named "A" for "{ATTR.A}"
out = str_replace("{ATTR." + A.name + "}", A.value, out);
}
// Evaluate operations.
out = math_replace(out);
// Handle newlines last so other substitutions can use them.
out = str_replace("\\n", "\n", out);
netlist[n++] = out;
}
}
// Process global SPICE data.
SCHEM.attributes(A) {
if (A.name == "SPICE") {
string out = A.value;
// Iterate over other global attributes.
SCHEM.attributes(B) {
out = str_replace("{ATTR." + B.name + "}", B.value, out);
}
// Evaluate operations.
out = math_replace(out);
// Handle newlines.
out = str_replace("\\n", "\n", out);
netlist[n++] = out;
}
}
// Open output file
output(outfile) {
printf(filesetext(filename(SCHEM.name), "\n"));
// Automatically run simulations. Must be first command.
printf(".CONTROL\nrun\n.ENDC\n");
printf(strjoin(models, '\n'));
printf(strjoin(netlist, '\n'));
}
/**
* Launch the file.
*
* Mac OS X
* --------
*
* The `open` command will handle everything perfectly.
*
* Windows
* -------
*
* This needs to be done with the `start` windows shell command to
* open the output file with the preferred application. It's a huge
* headache to use `start` directly from Eagle though. To make it,
* easier, we've included the `open.exe` utility, which accepts a
* filename argument.
*/
// Check for drive letter and colon to determine OS.
if (strchr(outfile, ':') == 1) {
// Windows host.
ret = system(filedir(argv[0]) + "open.exe \"" + outfile + "\"");
} else {
// Mac host.
ret = system("open \"" + outfile + "\"");
}
}
}
return ret;
}
Jump to Line
Something went wrong with that request. Please try again.