/// <summary> /// Constructor /// </summary> /// <param name="name">The name of the source to sweep</param> /// <param name="start">The starting value</param> /// <param name="stop">The stopping value</param> /// <param name="step">The step value</param> public Sweep(CircuitIdentifier name, double start, double stop, double step) : base() { ComponentName = name; Start = start; Stop = stop; Step = step; }
/// <summary> /// Generate a BSIM4 model /// </summary> /// <param name="name">Name</param> /// <param name="type">nmos or pmos</param> /// <param name="version">Version</param> /// <returns></returns> public static ICircuitObject GenerateBSIM4Model(CircuitIdentifier name, string type, string version) { double v = 4.8; switch (version) { case null: case "4.8.0": v = 4.8; break; default: if (!double.TryParse(version, System.Globalization.NumberStyles.Float, System.Globalization.CultureInfo.InvariantCulture, out v)) { throw new Exception("Unsupported version \"" + version + "\""); } break; } if (Math.Abs(v - 4.8) < 1e-12) { BSIM4v80Model b4v80 = new BSIM4v80Model(name); switch (type) { case "nmos": b4v80.SetNMOS(true); break; case "pmos": b4v80.SetPMOS(true); break; default: throw new Exception("Invalid type \"" + type + "\""); } return(b4v80); } else { throw new Exception("Invalid version \"" + v.ToString() + "\""); } }
/// <summary> /// Get both amplitude (in dB) and phase /// </summary> /// <param name="node">The node name</param> /// <param name="reference"></param> /// <param name="db">The voltage amplitude in decibels</param> /// <param name="phase">The phase in degrees</param> public void GetDbPhase(CircuitIdentifier node, CircuitIdentifier reference, out double db, out double phase) { Complex r = GetPhasor(node, reference); db = 10.0 * Math.Log10(r.Real * r.Real + r.Imaginary * r.Imaginary); phase = 180.0 / Math.PI * Math.Atan2(r.Imaginary, r.Real); }
/// <summary> /// Generate /// </summary> /// <param name="name">Name</param> /// <param name="parameters">Parameters</param> /// <param name="netlist">Netlist</param> /// <returns></returns> protected ICircuitObject GenerateVSW(CircuitIdentifier name, List <Token> parameters, Netlist netlist) { VoltageSwitch vsw = new VoltageSwitch(name); vsw.ReadNodes(netlist.Path, parameters); // Read the model if (parameters.Count < 5) { throw new ParseException(parameters[3], "Model expected", false); } vsw.SetModel(netlist.FindModel <VoltageSwitchModel>(parameters[4])); // Optional ON or OFF if (parameters.Count == 6) { switch (parameters[5].image.ToLower()) { case "on": vsw.SetOn(); break; case "off": vsw.SetOff(); break; default: throw new ParseException(parameters[5], "ON or OFF expected"); } } return(vsw); }
/// <summary> /// Generate a mutual inductance /// </summary> /// <param name="name">Name</param> /// <param name="parameters">Parameters</param> /// <param name="netlist">Netlist</param> /// <returns></returns> protected ICircuitObject GenerateMut(CircuitIdentifier name, List <Token> parameters, Netlist netlist) { MutualInductance mut = new MutualInductance(name); switch (parameters.Count) { case 0: throw new ParseException($"Inductor name expected for mutual inductance \"{name}\""); case 1: throw new ParseException(parameters[0], "Inductor name expected", false); case 2: throw new ParseException(parameters[1], "Coupling factor expected", false); } // Read two inductors if (!ReaderExtension.IsName(parameters[0])) { throw new ParseException(parameters[0], "Component name expected"); } mut.MUTind1 = new CircuitIdentifier(parameters[0].image); if (!ReaderExtension.IsName(parameters[1])) { throw new ParseException(parameters[1], "Component name expected"); } mut.MUTind2 = new CircuitIdentifier(parameters[1].image); mut.MUTcoupling.Set(netlist.ParseDouble(parameters[2])); return(mut); }
/// <summary> /// Get the voltage of a node in DC or Transient analysis /// </summary> /// <param name="node">The node name</param> /// <param name="reference">The reference (null if no reference)</param> /// <returns></returns> public double GetVoltage(CircuitIdentifier node, CircuitIdentifier reference = null) { double result = 0.0; // Get the positive node if (node == null) { throw new ArgumentNullException(nameof(node)); } if (Circuit.Nodes.Contains(node)) { int index = Circuit.Nodes[node].Index; result = Circuit.State.Real.Solution[index]; } else { throw new CircuitException($"Could not find node '{node}'"); } // Get the negative node if (reference != null) { if (Circuit.Nodes.Contains(reference)) { int index = Circuit.Nodes[reference].Index; result -= Circuit.State.Real.Solution[index]; } else { throw new CircuitException($"Could not find node '{reference}'"); } } return(result); }
/// <summary> /// Test a circuit object for all its parameters /// </summary> /// <param name="n">The netlist</param> /// <param name="name">The name of the object</param> /// <param name="names">The parameter names</param> /// <param name="values">The parameter values</param> /// <param name="nodes">The nodes (optional)</param> /// <returns></returns> protected T Test <T>(Netlist n, CircuitIdentifier name, string[] names = null, double[] values = null, CircuitIdentifier[] nodes = null) { var obj = n.Circuit.Objects[name]; Assert.AreEqual(typeof(T), obj.GetType()); // Test all parameters if (names != null) { TestParameters((IParameterized)obj, names, values); } // Test all nodes if (nodes != null) { TestNodes((ICircuitComponent)obj, nodes); } // Make sure there are no warnings if (CircuitWarning.Warnings.Count > 0) { throw new Exception("Warning: " + CircuitWarning.Warnings[0]); } return((T)obj); }
/// <summary> /// Read /// </summary> /// <param name="type">Type</param> /// <param name="st">Statement</param> /// <param name="netlist">Netlist</param> /// <returns></returns> public override bool Read(string type, Statement st, Netlist netlist) { // Apply the change to the circuit CircuitIdentifier id; if (netlist.Path.InstancePath != null) { id = netlist.Path.InstancePath.Grow(st.Name.image); } else { id = new CircuitIdentifier(st.Name.image); } ICircuitObject result = Generate(type, id, st.Parameters, netlist); Generated = result; if (result != null) { // Add the circuit component netlist.Circuit.Objects.Add(result); return(true); } else { return(false); } }
/// <summary> /// Constructor /// </summary> /// <param name="name">The name of the current controlled current source</param> /// <param name="pos">The positive node</param> /// <param name="neg">The negative node</param> /// <param name="vsource">The name of the voltage source</param> /// <param name="gain">The current gain</param> public CurrentControlledCurrentsource(CircuitIdentifier name, CircuitIdentifier pos, CircuitIdentifier neg, CircuitIdentifier vsource, double gain) : base(name) { Priority = -1; Connect(pos, neg); CCCScoeff.Set(gain); CCCScontName = vsource; }
/// <summary> /// Generate the mosfet instance /// </summary> /// <param name="type">Type</param> /// <param name="name">Name</param> /// <param name="parameters">Parameters</param> /// <param name="netlist">Netlist</param> /// <returns></returns> protected override ICircuitObject Generate(string type, CircuitIdentifier name, List <Token> parameters, Netlist netlist) { // Errors switch (parameters.Count) { case 0: throw new ParseException($"Node expected for component {name}"); case 1: case 2: case 3: throw new ParseException(parameters[parameters.Count - 1], "Node expected", false); case 4: throw new ParseException(parameters[3], "Model name expected"); } // Get the model and generate a component for it ICircuitObject model = netlist.Path.FindModel <ICircuitObject>(netlist.Circuit.Objects, new CircuitIdentifier(parameters[4].image)); ICircuitComponent mosfet = null; if (Mosfets.ContainsKey(model.GetType())) { mosfet = Mosfets[model.GetType()].Invoke(name, model); } else { throw new ParseException(parameters[4], "Invalid model"); } // The rest is all just parameters mosfet.ReadNodes(netlist.Path, parameters); netlist.ReadParameters((IParameterized)mosfet, parameters, 4); return(mosfet); }
/// <summary> /// Constructor /// </summary> /// <param name="definition">The definition</param> /// <param name="name">Name identifier</param> public Subcircuit(SubcircuitDefinition definition, CircuitIdentifier name, List <CircuitIdentifier> pins, Dictionary <CircuitIdentifier, Token> parameters = null) { Definition = definition; Name = name; Pins = pins; Parameters = parameters; }
/// <summary> /// Find a model using the current scope rules /// </summary> /// <param name="id">Identifier of the model</param> /// <returns></returns> public T FindModel <T>(CircuitObjects obj, CircuitIdentifier id) where T : class, ICircuitObject { ICircuitObject co; T model = null; CircuitIdentifier orig = id; switch (ModelScope) { case ScopeRule.Descend: // Find the identifier in all parent paths, starting at the current level while (id != null) { // Try to find the subcircuit if (obj.TryGetObject(id, out co)) { model = co as T; if (model != null) { return(model); } } // We didn't find the model yet, so try the parent path id = id.Shrink(); } break; case ScopeRule.GlobalLocal: // Find the model locally if (obj.TryGetObject(id, out co)) { model = co as T; if (model != null) { return(model); } } // Find the model globally if (id.Path.Length > 1) { id = new CircuitIdentifier(id.Name); if (obj.TryGetObject(id, out co)) { model = co as T; if (model != null) { return(model); } } } break; } throw new ParseException($"Cannot find model \"{orig}\""); }
/// <summary> /// Generate a switch /// </summary> /// <param name="type">Type</param> /// <param name="name">Name</param> /// <param name="parameters">Parameters</param> /// <param name="netlist">Netlist</param> /// <returns></returns> protected override ICircuitObject Generate(string type, CircuitIdentifier name, List <Token> parameters, Netlist netlist) { switch (type) { case "s": return(GenerateVSW(name, parameters, netlist)); case "w": return(GenerateCSW(name, parameters, netlist)); } return(null); }
/// <summary> /// Test the nodes of a circuitcomponent /// </summary> /// <param name="c">The component</param> /// <param name="nodes">The expected node names</param> protected void TestNodes(ICircuitComponent c, CircuitIdentifier[] nodes) { // Test all nodes for (int i = 0; i < nodes.Length; i++) { CircuitIdentifier expected = nodes[i]; CircuitIdentifier actual = c.GetNode(i); Assert.AreEqual(expected, actual); } }
/// <summary> /// Generate a new model /// </summary> /// <param name="name">The model name</param> /// <param name="type">The type</param> /// <returns></returns> protected override ICircuitObject GenerateModel(CircuitIdentifier name, string type) { switch (type) { case "r": return(new ResistorModel(name)); case "c": return(new CapacitorModel(name)); } return(null); }
/// <summary> /// Read /// </summary> /// <param name="name">Name</param> /// <param name="parameters">Parameters</param> /// <param name="netlist">Netlist</param> /// <returns></returns> public override bool Read(string type, Statement st, Netlist netlist) { // Get the nodes CircuitIdentifier node, reference = null; switch (st.Parameters.Count) { case 0: throw new ParseException(st.Name, "Node expected", false); case 2: if (!ReaderExtension.IsNode(st.Parameters[1])) { throw new ParseException(st.Parameters[1], "Node expected"); } reference = new CircuitIdentifier(st.Parameters[1].image); goto case 1; case 1: if (!ReaderExtension.IsNode(st.Parameters[0])) { throw new ParseException(st.Parameters[0], "Node expected"); } node = new CircuitIdentifier(st.Parameters[0].image); break; default: throw new ParseException(st.Name, "Too many nodes specified", false); } // Add to the exports Export ve = null; switch (type) { case "v": ve = new VoltageExport(node, reference); break; case "vr": ve = new VoltageRealExport(node, reference); break; case "vi": ve = new VoltageImaginaryExport(node, reference); break; case "vdb": ve = new VoltageDecibelExport(node, reference); break; case "vp": ve = new VoltagePhaseExport(node, reference); break; } if (ve != null) { netlist.Exports.Add(ve); } Generated = ve; return(true); }
/// <summary> /// Generate /// </summary> /// <param name="name">Name</param> /// <param name="parameters">Parameters</param> /// <param name="netlist">Netlist</param> /// <returns></returns> protected ICircuitObject GenerateVCCS(CircuitIdentifier name, List <Token> parameters, Netlist netlist) { VoltageControlledCurrentsource vccs = new VoltageControlledCurrentsource(name); vccs.ReadNodes(netlist.Path, parameters); if (parameters.Count < 5) { throw new ParseException(parameters[3], "Value expected", false); } vccs.VCCScoeff.Set(netlist.ParseDouble(parameters[4])); return(vccs); }
/// <summary> /// Generate the resistor, inductor, capacitor or mutual inductance /// </summary> /// <param name="type">Type</param> /// <param name="name">Name</param> /// <param name="parameters">Parameters</param> /// <param name="netlist">Netlist</param> /// <returns></returns> protected override ICircuitObject Generate(string type, CircuitIdentifier name, List <Token> parameters, Netlist netlist) { switch (type) { case "r": return(GenerateRes(name, parameters, netlist)); case "l": return(GenerateInd(name, parameters, netlist)); case "c": return(GenerateCap(name, parameters, netlist)); case "k": return(GenerateMut(name, parameters, netlist)); } return(null); }
/// <summary> /// Generate a current source /// </summary> /// <param name="name">Name</param> /// <param name="parameters">Parameters</param> /// <param name="netlist">Netlist</param> /// <returns></returns> protected ICircuitObject GenerateISRC(CircuitIdentifier name, List <Token> parameters, Netlist netlist) { Currentsource isrc = new Currentsource(name); isrc.ReadNodes(netlist.Path, parameters); // We can have a value or just DC for (int i = 2; i < parameters.Count; i++) { // DC specification if (i == 2 && parameters[i].image.ToLower() == "dc") { i++; isrc.ISRCdcValue.Set(netlist.ParseDouble(parameters[i])); } else if (i == 2 && ReaderExtension.IsValue(parameters[i])) { isrc.ISRCdcValue.Set(netlist.ParseDouble(parameters[i])); } // AC specification else if (parameters[i].image.ToLower() == "ac") { i++; isrc.ISRCacMag.Set(netlist.ParseDouble(parameters[i])); // Look forward for one more value if (i + 1 < parameters.Count && ReaderExtension.IsValue(parameters[i + 1])) { i++; isrc.ISRCacPhase.Set(netlist.ParseDouble(parameters[i])); } } // Waveforms else if (parameters[i].kind == BRACKET) { // Find the reader BracketToken bt = parameters[i] as BracketToken; Statement st = new Statement(StatementType.Waveform, bt.Name, bt.Parameters); isrc.ISRCwaveform = (IWaveform)netlist.Readers.Read(st, netlist); } else { throw new ParseException(parameters[i], "Unrecognized parameter"); } } return(isrc); }
/// <summary> /// Generate a new model /// </summary> /// <param name="name">Model name</param> /// <param name="type">Model type</param> /// <returns></returns> protected override ICircuitObject GenerateModel(CircuitIdentifier name, string type) { BJTModel model = new BJTModel(name); if (type == "npn") { model.SetNPN(true); } else if (type == "pnp") { model.SetPNP(true); } return(model); }
/// <summary> /// Generate a BSIM3 model /// </summary> /// <param name="name">Name of the parameter</param> /// <param name="type">Type of the parameter</param> /// <param name="version">Version of the parameter</param> /// <returns></returns> public static ICircuitObject GenerateBSIM3Model(CircuitIdentifier name, string type, string version) { double v = 3.3; switch (version) { case null: case "3.3.0": v = 3.3; break; case "3.2.4": v = 3.24; break; default: if (!double.TryParse(version, System.Globalization.NumberStyles.Float, System.Globalization.CultureInfo.InvariantCulture, out v)) { throw new Exception("Unsupported version \"" + version + "\""); } break; } if (Math.Abs(v - 3.3) < 1e-12) { var b3v30 = new BSIM3v30Model(name); switch (type) { case "nmos": b3v30.SetNMOS(true); break; case "pmos": b3v30.SetPMOS(true); break; default: throw new Exception("Invalid type \"" + type + "\""); } return(b3v30); } else if (Math.Abs(v - 3.24) < 1e-12) { var b3v24 = new BSIM3v24Model(name); switch (type) { case "nmos": b3v24.SetNMOS(true); break; case "pmos": b3v24.SetPMOS(true); break; default: throw new Exception("Invalid type \"" + type + "\""); } return(b3v24); } else { throw new Exception("Unrecognized version \"" + v.ToString() + "\""); } }
/// <summary> /// Generate a capacitor /// </summary> /// <param name="name">Name</param> /// <param name="parameters">Parameters</param> /// <param name="netlist">Netlist</param> /// <returns></returns> protected ICircuitObject GenerateCap(CircuitIdentifier name, List <Token> parameters, Netlist netlist) { Capacitor cap = new Capacitor(name); cap.ReadNodes(netlist.Path, parameters); // Search for a parameter IC, which is common for both types of capacitors for (int i = 3; i < parameters.Count; i++) { if (parameters[i].kind == ASSIGNMENT) { AssignmentToken at = parameters[i] as AssignmentToken; if (at.Name.image.ToLower() == "ic") { double ic = netlist.ParseDouble(at.Value); cap.CAPinitCond.Set(ic); parameters.RemoveAt(i); break; } } } // The rest is just dependent on the number of parameters if (parameters.Count == 3) { cap.CAPcapac.Set(netlist.ParseDouble(parameters[2])); } else { cap.SetModel(netlist.FindModel <CapacitorModel>(parameters[2])); switch (parameters[2].kind) { case WORD: case IDENTIFIER: cap.SetModel(netlist.Path.FindModel <CapacitorModel>(netlist.Circuit.Objects, new CircuitIdentifier(parameters[2].image))); break; default: throw new ParseException(parameters[2], "Model name expected"); } netlist.ReadParameters(cap, parameters, 2); if (!cap.CAPlength.Given) { throw new ParseException(parameters[1], "L needs to be specified", false); } } return(cap); }
/// <summary> /// BSIM1 model generator /// Version is ignored /// </summary> /// <param name="name">Name</param> /// <param name="type">nmos or pmos</param> /// <param name="version">Version</param> /// <returns></returns> public static ICircuitObject GenerateBSIM1Model(CircuitIdentifier name, string type, string version) { BSIM1Model model = new BSIM1Model(name); switch (type) { case "nmos": model.SetNMOS(true); break; case "pmos": model.SetPMOS(true); break; default: throw new Exception("Invalid type \"" + type + "\""); } return(model); }
/// <summary> /// Constructor /// </summary> /// <param name="name">The name of the component</param> public CircuitComponent(CircuitIdentifier name) : base() { Name = name; if (pins != null) { connections = new CircuitIdentifier[pins.Length]; indices = new int[pins.Length]; } else { connections = null; indices = null; } }
/// <summary> /// Generate an inductor /// </summary> /// <param name="name">Name</param> /// <param name="parameters">Parameters</param> /// <param name="netlist">Netlist</param> /// <returns></returns> protected ICircuitObject GenerateInd(CircuitIdentifier name, List <Token> parameters, Netlist netlist) { Inductor ind = new Inductor(name); ind.ReadNodes(netlist.Path, parameters); // Read the value if (parameters.Count < 3) { throw new ParseException(parameters[1], "Inductance expected", false); } ind.INDinduct.Set(netlist.ParseDouble(parameters[2])); // Read initial conditions netlist.ReadParameters(ind, parameters, 3); return(ind); }
/// <summary> /// Read /// </summary> /// <param name="name">Name</param> /// <param name="parameters">Parameters</param> /// <param name="netlist">Netlist</param> /// <returns></returns> public override bool Read(string type, Statement st, Netlist netlist) { // Get the source name CircuitIdentifier source; switch (st.Parameters.Count) { case 0: throw new ParseException(st.Name, "Voltage source expected", false); case 1: if (!ReaderExtension.IsName(st.Parameters[0])) { throw new ParseException(st.Parameters[0], "Component name expected"); } source = new CircuitIdentifier(st.Parameters[0].image); break; default: throw new ParseException(st.Name, "Too many nodes specified", false); } // Add to the exports Export ce = null; switch (type) { case "i": ce = new CurrentExport(source); break; case "ir": ce = new CurrentRealExport(source); break; case "ii": ce = new CurrentImaginaryExport(source); break; case "im": ce = new CurrentMagnitudeExport(source); break; case "ip": ce = new CurrentPhaseExport(source); break; case "idb": ce = new CurrentDecibelExport(source); break; } if (ce != null) { netlist.Exports.Add(ce); } Generated = ce; return(true); }
/// <summary> /// Generate a current switch /// </summary> /// <param name="name">The name</param> /// <param name="parameters">Parameters</param> /// <param name="netlist">Netlist</param> /// <returns></returns> protected ICircuitObject GenerateCSW(CircuitIdentifier name, List <Token> parameters, Netlist netlist) { CurrentSwitch csw = new CurrentSwitch(name); csw.ReadNodes(netlist.Path, parameters); switch (parameters.Count) { case 2: throw new ParseException(parameters[1], "Voltage source expected", false); case 3: throw new ParseException(parameters[2], "Model expected", false); } // Get the controlling voltage source switch (parameters[2].kind) { case WORD: csw.CSWcontName = new CircuitIdentifier(parameters[2].image); break; default: throw new ParseException(parameters[2], "Voltage source name expected"); } // Get the model csw.SetModel(netlist.FindModel <CurrentSwitchModel>(parameters[3])); // Optional on or off if (parameters.Count > 4) { switch (parameters[4].image.ToLower()) { case "on": csw.SetOn(); break; case "off": csw.SetOff(); break; default: throw new ParseException(parameters[4], "ON or OFF expected"); } } return(csw); }
/// <summary> /// Find a subcircuit definition using the current scope rules /// </summary> /// <param name="id">Subcircuit definition identifier</param> /// <returns></returns> public SubcircuitDefinition FindDefinition(Dictionary <CircuitIdentifier, SubcircuitDefinition> definitions, CircuitIdentifier id) { SubcircuitDefinition result = null; CircuitIdentifier orig = id; switch (DefinitionScope) { case ScopeRule.Descend: while (id != null) { // Try to find the definition if (definitions.TryGetValue(id, out result)) { return(result); } // Not found, go to the parent path id = id.Shrink(); } break; case ScopeRule.GlobalLocal: // Try to find the definition locally if (definitions.TryGetValue(id, out result)) { return(result); } // Try to find the definition globally if (id.Path.Length > 1) { id = new CircuitIdentifier(id.Name); if (definitions.TryGetValue(id, out result)) { return(result); } } break; } throw new ParseException($"Cannot find subcircuit \"{orig}\""); }
/// <summary> /// Read nodes for a component /// </summary> /// <param name="c">The circuit component</param> /// <param name="parameters">The parameters</param> /// <param name="index">The index where to start reading, defaults to 0</param> public static void ReadNodes(this ICircuitComponent c, SubcircuitPath path, List <Token> parameters, int index = 0) { int count = c.PinCount; // Check that there are enough parameters left to read if (parameters.Count < index + count) { if (parameters.Count > 0) { throw new ParseException(parameters[parameters.Count - 1], $"{count} nodes expected"); } throw new ParseException($"Unexpected end of line, {count} nodes expected"); } // Extract the nodes CircuitIdentifier[] nodes = new CircuitIdentifier[count]; for (int i = index; i < index + count; i++) { if (IsNode(parameters[i])) { // Map to a new node if necessary, else make the node local to the current path CircuitIdentifier node = new CircuitIdentifier(parameters[i].image); if (path.NodeMap.TryGetValue(node, out CircuitIdentifier mapped)) { node = mapped; } else if (path.InstancePath != null) { node = path.InstancePath.Grow(node); } // Store the node nodes[i] = node; } else { throw new ParseException(parameters[i], "Node expected"); } } // Succeeded c.Connect(nodes); }
/// <summary> /// Generate a CCCS /// </summary> /// <param name="name">Name</param> /// <param name="parameters">Parameters</param> /// <param name="netlist">Netlist</param> /// <returns></returns> protected ICircuitObject GenerateCCCS(CircuitIdentifier name, List <Token> parameters, Netlist netlist) { CurrentControlledCurrentsource cccs = new CurrentControlledCurrentsource(name); cccs.ReadNodes(netlist.Path, parameters); switch (parameters.Count) { case 2: throw new ParseException(parameters[1], "Voltage source expected", false); case 3: throw new ParseException(parameters[2], "Value expected", false); } if (!ReaderExtension.IsName(parameters[2])) { throw new ParseException(parameters[2], "Component name expected"); } cccs.CCCScontName = new CircuitIdentifier(parameters[2].image); cccs.CCCScoeff.Set(netlist.ParseDouble(parameters[3])); return(cccs); }