From b1839a8d8f3acfce1fb880931a25a8d24fada2a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcin=20Go=C5=82=C4=99biowski?= Date: Sat, 27 Dec 2025 12:03:46 +0100 Subject: [PATCH 1/5] Expose Levels and Mosfets --- .../Components/Semiconductors/MosfetGenerator.cs | 13 ++++++++++++- .../EntityGenerators/Models/MosfetModelGenerator.cs | 7 ++++++- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/src/SpiceSharpParser/ModelReaders/Netlist/Spice/Readers/EntityGenerators/Components/Semiconductors/MosfetGenerator.cs b/src/SpiceSharpParser/ModelReaders/Netlist/Spice/Readers/EntityGenerators/Components/Semiconductors/MosfetGenerator.cs index 474dc50d..eff7fd7b 100644 --- a/src/SpiceSharpParser/ModelReaders/Netlist/Spice/Readers/EntityGenerators/Components/Semiconductors/MosfetGenerator.cs +++ b/src/SpiceSharpParser/ModelReaders/Netlist/Spice/Readers/EntityGenerators/Components/Semiconductors/MosfetGenerator.cs @@ -35,7 +35,18 @@ public MosfetGenerator() }); } - protected Dictionary> Mosfets { get; } = new (); + protected static Dictionary> Mosfets { get; } = new (); + + public static void AddMosfet() + where TMosfet : SpiceSharp.Components.Component + where TModel : Context.Models.Model + { + Mosfets.Add(typeof(TModel), (name) => + { + var mosfet = (TMosfet)Activator.CreateInstance(typeof(TMosfet), name); + return new MosfetDetails { Mosfet = mosfet, SetModelAction = (model) => mosfet.Model = model.Name }; + }); + } public override IEntity Generate(string componentIdentifier, string originalName, string type, ParameterCollection parameters, IReadingContext context) { diff --git a/src/SpiceSharpParser/ModelReaders/Netlist/Spice/Readers/EntityGenerators/Models/MosfetModelGenerator.cs b/src/SpiceSharpParser/ModelReaders/Netlist/Spice/Readers/EntityGenerators/Models/MosfetModelGenerator.cs index d4e13ef5..37ca90b6 100644 --- a/src/SpiceSharpParser/ModelReaders/Netlist/Spice/Readers/EntityGenerators/Models/MosfetModelGenerator.cs +++ b/src/SpiceSharpParser/ModelReaders/Netlist/Spice/Readers/EntityGenerators/Models/MosfetModelGenerator.cs @@ -54,7 +54,12 @@ public MosfetModelGenerator() /// Gets available model generators indexed by their LEVEL. /// The parameters passed are name, type (nmos or pmos) and the version. /// - protected Dictionary> Levels { get; } = new Dictionary>(); + protected static Dictionary> Levels { get; } = new Dictionary>(); + + public static void AddLevel(int level, Func generator) + { + Levels[level] = generator; + } public override Context.Models.Model Generate(string id, string type, ParameterCollection parameters, IReadingContext context) { From f470714c07d7537d6b0cf401318ecadbc5770c60 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcin=20Go=C5=82=C4=99biowski?= Date: Sat, 27 Dec 2025 12:09:10 +0100 Subject: [PATCH 2/5] Better idea --- .../Models/MosfetModelGenerator.cs | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/src/SpiceSharpParser/ModelReaders/Netlist/Spice/Readers/EntityGenerators/Models/MosfetModelGenerator.cs b/src/SpiceSharpParser/ModelReaders/Netlist/Spice/Readers/EntityGenerators/Models/MosfetModelGenerator.cs index 37ca90b6..18d89881 100644 --- a/src/SpiceSharpParser/ModelReaders/Netlist/Spice/Readers/EntityGenerators/Models/MosfetModelGenerator.cs +++ b/src/SpiceSharpParser/ModelReaders/Netlist/Spice/Readers/EntityGenerators/Models/MosfetModelGenerator.cs @@ -1,6 +1,8 @@ using System; using System.Collections.Generic; using SpiceSharp.Components; +using SpiceSharp.Components.Mosfets; +using SpiceSharp.Entities; using SpiceSharpParser.Common.Validation; using SpiceSharpParser.ModelReaders.Netlist.Spice.Context; using SpiceSharpParser.Models.Netlist.Spice.Objects; @@ -56,9 +58,20 @@ public MosfetModelGenerator() /// protected static Dictionary> Levels { get; } = new Dictionary>(); - public static void AddLevel(int level, Func generator) + public static void AddLevel(int level) + where TModel : Entity { - Levels[level] = generator; + Levels[level] = (name, type, _) => + { + var mosfet = (TModel)Activator.CreateInstance(typeof(TModel), name); + switch (type.ToLower()) + { + case "nmos": mosfet.SetParameter("nmos", true); break; + case "pmos": mosfet.SetParameter("pmos", true); break; + } + + return new Context.Models.Model(name, mosfet, mosfet.Parameters); + }; } public override Context.Models.Model Generate(string id, string type, ParameterCollection parameters, IReadingContext context) From 3371ab1d39bc7c5b24978e5e6b8cbd940163ba9e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcin=20Go=C5=82=C4=99biowski?= Date: Sat, 27 Dec 2025 12:31:13 +0100 Subject: [PATCH 3/5] Fixes --- .../Extensions/CustomMosfetModelTest.cs | 18 +++++++++++++++--- .../Semiconductors/MosfetGenerator.cs | 5 ++--- .../Models/MosfetModelGenerator.cs | 8 +++++--- 3 files changed, 22 insertions(+), 9 deletions(-) diff --git a/src/SpiceSharpParser.IntegrationTests/Examples/Extensions/CustomMosfetModelTest.cs b/src/SpiceSharpParser.IntegrationTests/Examples/Extensions/CustomMosfetModelTest.cs index 9cbda205..b9f6cb70 100644 --- a/src/SpiceSharpParser.IntegrationTests/Examples/Extensions/CustomMosfetModelTest.cs +++ b/src/SpiceSharpParser.IntegrationTests/Examples/Extensions/CustomMosfetModelTest.cs @@ -1,4 +1,6 @@ -using System.IO; +using SpiceSharpParser.ModelReaders.Netlist.Spice.Readers.EntityGenerators.Components.Semiconductors; +using SpiceSharpParser.ModelReaders.Netlist.Spice.Readers.EntityGenerators.Models; +using System.IO; using Xunit; namespace SpiceSharpParser.IntegrationTests.Examples.Extensions @@ -18,9 +20,19 @@ public void When_CustomMosfetModel_Used_NoExceptions() // Convert to Spice# var spiceSharpReader = new SpiceSharpReader(); spiceSharpReader.Settings.CaseSensitivity.IsModelTypeCaseSensitive = false; - spiceSharpReader.Settings.Mappings.Models.Map(new[] { "PMOS", "NMOS" }, new CustomMosfetModelGenerator()); - var spiceSharpModel = spiceSharpReader.Read(parseResult.FinalModel); + + // custom mappings + var modelGenerator = new MosfetModelGenerator(); + modelGenerator.AddLevel(39); + spiceSharpReader.Settings.Mappings.Models.Map(new[] { "PMOS", "NMOS" }, modelGenerator); + var mosfetGenerator = new MosfetGenerator(); + mosfetGenerator.AddMosfet(); + spiceSharpReader.Settings.Mappings.Components.Map("M", mosfetGenerator); + + + var spiceSharpModel = spiceSharpReader.Read(parseResult.FinalModel); + Assert.False(spiceSharpModel.ValidationResult.HasError); Assert.False(spiceSharpModel.ValidationResult.HasWarning); } diff --git a/src/SpiceSharpParser/ModelReaders/Netlist/Spice/Readers/EntityGenerators/Components/Semiconductors/MosfetGenerator.cs b/src/SpiceSharpParser/ModelReaders/Netlist/Spice/Readers/EntityGenerators/Components/Semiconductors/MosfetGenerator.cs index eff7fd7b..0ebecbf9 100644 --- a/src/SpiceSharpParser/ModelReaders/Netlist/Spice/Readers/EntityGenerators/Components/Semiconductors/MosfetGenerator.cs +++ b/src/SpiceSharpParser/ModelReaders/Netlist/Spice/Readers/EntityGenerators/Components/Semiconductors/MosfetGenerator.cs @@ -35,11 +35,10 @@ public MosfetGenerator() }); } - protected static Dictionary> Mosfets { get; } = new (); + protected Dictionary> Mosfets { get; } = new (); - public static void AddMosfet() + public void AddMosfet() where TMosfet : SpiceSharp.Components.Component - where TModel : Context.Models.Model { Mosfets.Add(typeof(TModel), (name) => { diff --git a/src/SpiceSharpParser/ModelReaders/Netlist/Spice/Readers/EntityGenerators/Models/MosfetModelGenerator.cs b/src/SpiceSharpParser/ModelReaders/Netlist/Spice/Readers/EntityGenerators/Models/MosfetModelGenerator.cs index 18d89881..ffe5967c 100644 --- a/src/SpiceSharpParser/ModelReaders/Netlist/Spice/Readers/EntityGenerators/Models/MosfetModelGenerator.cs +++ b/src/SpiceSharpParser/ModelReaders/Netlist/Spice/Readers/EntityGenerators/Models/MosfetModelGenerator.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using SpiceSharp; using SpiceSharp.Components; using SpiceSharp.Components.Mosfets; using SpiceSharp.Entities; @@ -56,10 +57,11 @@ public MosfetModelGenerator() /// Gets available model generators indexed by their LEVEL. /// The parameters passed are name, type (nmos or pmos) and the version. /// - protected static Dictionary> Levels { get; } = new Dictionary>(); + protected Dictionary> Levels { get; } = new Dictionary>(); - public static void AddLevel(int level) - where TModel : Entity + public void AddLevel(int level) + where TModel : Entity + where TParameters : ModelParameters, ICloneable, new() { Levels[level] = (name, type, _) => { From cece842c6c557a87d82aaf0e6c93e6b13ca0be7d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcin=20Go=C5=82=C4=99biowski?= Date: Sat, 27 Dec 2025 12:35:53 +0100 Subject: [PATCH 4/5] Fix --- .../Components/Semiconductors/MosfetGenerator.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/SpiceSharpParser/ModelReaders/Netlist/Spice/Readers/EntityGenerators/Components/Semiconductors/MosfetGenerator.cs b/src/SpiceSharpParser/ModelReaders/Netlist/Spice/Readers/EntityGenerators/Components/Semiconductors/MosfetGenerator.cs index 0ebecbf9..3be4c2fc 100644 --- a/src/SpiceSharpParser/ModelReaders/Netlist/Spice/Readers/EntityGenerators/Components/Semiconductors/MosfetGenerator.cs +++ b/src/SpiceSharpParser/ModelReaders/Netlist/Spice/Readers/EntityGenerators/Components/Semiconductors/MosfetGenerator.cs @@ -40,11 +40,11 @@ public MosfetGenerator() public void AddMosfet() where TMosfet : SpiceSharp.Components.Component { - Mosfets.Add(typeof(TModel), (name) => + Mosfets[typeof(TModel)] = (name) => { var mosfet = (TMosfet)Activator.CreateInstance(typeof(TMosfet), name); return new MosfetDetails { Mosfet = mosfet, SetModelAction = (model) => mosfet.Model = model.Name }; - }); + }; } public override IEntity Generate(string componentIdentifier, string originalName, string type, ParameterCollection parameters, IReadingContext context) From 5bbe9538fc570eb76112003a854258363ff8c4d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcin=20Go=C5=82=C4=99biowski?= Date: Sat, 27 Dec 2025 12:39:53 +0100 Subject: [PATCH 5/5] Fix tests --- .../Examples/Circuits/MosfetExample2.cir | 4 +++ .../Extensions/CustomMosfetModelTest.cs | 26 ++++++++++++++++--- .../SpiceSharpParser.IntegrationTests.csproj | 3 +++ 3 files changed, 29 insertions(+), 4 deletions(-) create mode 100644 src/SpiceSharpParser.IntegrationTests/Examples/Circuits/MosfetExample2.cir diff --git a/src/SpiceSharpParser.IntegrationTests/Examples/Circuits/MosfetExample2.cir b/src/SpiceSharpParser.IntegrationTests/Examples/Circuits/MosfetExample2.cir new file mode 100644 index 00000000..a60eeb02 --- /dev/null +++ b/src/SpiceSharpParser.IntegrationTests/Examples/Circuits/MosfetExample2.cir @@ -0,0 +1,4 @@ +Mosfet circuit +Md 0 1 2 3 my-pmos +.model my-pmos pmos(level = 3) +.END diff --git a/src/SpiceSharpParser.IntegrationTests/Examples/Extensions/CustomMosfetModelTest.cs b/src/SpiceSharpParser.IntegrationTests/Examples/Extensions/CustomMosfetModelTest.cs index b9f6cb70..4be7dc64 100644 --- a/src/SpiceSharpParser.IntegrationTests/Examples/Extensions/CustomMosfetModelTest.cs +++ b/src/SpiceSharpParser.IntegrationTests/Examples/Extensions/CustomMosfetModelTest.cs @@ -20,19 +20,37 @@ public void When_CustomMosfetModel_Used_NoExceptions() // Convert to Spice# var spiceSharpReader = new SpiceSharpReader(); spiceSharpReader.Settings.CaseSensitivity.IsModelTypeCaseSensitive = false; + spiceSharpReader.Settings.Mappings.Models.Map(new[] { "PMOS", "NMOS" }, new CustomMosfetModelGenerator()); + var spiceSharpModel = spiceSharpReader.Read(parseResult.FinalModel); + + Assert.False(spiceSharpModel.ValidationResult.HasError); + Assert.False(spiceSharpModel.ValidationResult.HasWarning); + } + [Fact] + public void When_CustomMosfetModel2_Used_NoExceptions() + { + // Create a model from text file + string path = Path.Combine(Directory.GetCurrentDirectory(), "Examples/Circuits/MosfetExample2.cir"); + var netlistContent = File.ReadAllText(path); + var parser = new SpiceNetlistParser(); + parser.Settings.Lexing.HasTitle = true; + var parseResult = parser.ParseNetlist(netlistContent); + // Convert to Spice# + var spiceSharpReader = new SpiceSharpReader(); + spiceSharpReader.Settings.CaseSensitivity.IsModelTypeCaseSensitive = false; - // custom mappings + // custom mosfet models var modelGenerator = new MosfetModelGenerator(); - modelGenerator.AddLevel(39); + modelGenerator.AddLevel(4); spiceSharpReader.Settings.Mappings.Models.Map(new[] { "PMOS", "NMOS" }, modelGenerator); var mosfetGenerator = new MosfetGenerator(); - mosfetGenerator.AddMosfet(); + mosfetGenerator.AddMosfet(); spiceSharpReader.Settings.Mappings.Components.Map("M", mosfetGenerator); var spiceSharpModel = spiceSharpReader.Read(parseResult.FinalModel); - + Assert.False(spiceSharpModel.ValidationResult.HasError); Assert.False(spiceSharpModel.ValidationResult.HasWarning); } diff --git a/src/SpiceSharpParser.IntegrationTests/SpiceSharpParser.IntegrationTests.csproj b/src/SpiceSharpParser.IntegrationTests/SpiceSharpParser.IntegrationTests.csproj index 9bad3f35..ab22d991 100644 --- a/src/SpiceSharpParser.IntegrationTests/SpiceSharpParser.IntegrationTests.csproj +++ b/src/SpiceSharpParser.IntegrationTests/SpiceSharpParser.IntegrationTests.csproj @@ -96,6 +96,9 @@ Always + + Always + Always