From 510cd41b456b7867b0940d4ce34270b83c772622 Mon Sep 17 00:00:00 2001 From: jClugstor Date: Fri, 28 Mar 2025 11:46:11 -0400 Subject: [PATCH 01/14] add MOSFETs --- src/Electrical/Analog/mosfets.jl | 180 +++++++++++++++++++++++++++++++ 1 file changed, 180 insertions(+) create mode 100644 src/Electrical/Analog/mosfets.jl diff --git a/src/Electrical/Analog/mosfets.jl b/src/Electrical/Analog/mosfets.jl new file mode 100644 index 00000000..1b48b8d5 --- /dev/null +++ b/src/Electrical/Analog/mosfets.jl @@ -0,0 +1,180 @@ +""" + NMOS(;name, V_tn, R_DS, lambda) + +Creates an N-type MOSFET transistor + + # Structural Parameters + - `use_transconductance`: If `true` the parameter `k_n` needs to be provided, and is used in the calculation of the current + through the transistor. Otherwise, `mu_n`, `C_ox`, `W`, and `L` need to be provided and are used to calculate the transconductance. + + - `use_channel_length_modulation`: If `true` the channel length modulation effect is taken in to account. In essence this gives + the drain-source current has a small dependency on the drains-source voltage in the saturation region of operation. + + # Connectors + - `d` Drain Pin + - `g` Gate Pin + - `s` Source Pin + + # Parameters + - `mu_n`: Electron mobility + - `C_ox`: Oxide capacitance (F/m^2) + - `W`: Channel width (m) + - `L`: Channel length + - `k_n`: MOSFET transconductance parameter + +Based on the MOSFET models in (Sedra, A. S., Smith, K. C., Carusone, T. C., & Gaudet, V. C. (2021). Microelectronic circuits (8th ed.). Oxford University Press.) +""" +@mtkmodel NMOS begin + @variables begin + V_GS(t) + V_DS(t) + V_OV(t) + end + + @components begin + d = Pin(), [description = "Drain pin"] + g = Pin(), [description = "Gate pin"] + s = Pin(), [description = "Source pin"] + end + + @parameters begin + V_tn = 0.8, [description = "Threshold voltage (V)"] + R_DS = 1e7, [description = "Drain to source resistance (Ω)"] + + if use_channel_length_modulation + lambda = 1 / 25, [description = "Channel length modulation coefficient (V^(-1))"] + end + + if !use_transconductance + mu_n, [description = "Electron mobility"] + C_ox, [description = "Oxide capacitance (F/m^2)"] + W, [description = "Channel width (m)"] + L, [description = "Channel length (m)"] + else + k_n = 20e-3, [description = "MOSFET transconductance parameter"] + end + end + + @structural_parameters begin + use_transconductance = true + use_channel_length_modulation = true + end + + begin + if !use_transconductance + k_n = mu_n * C_ox * (W / L) + end + end + + @equations begin + V_DS ~ ifelse(d.v < s.v, s.v - d.v, d.v - s.v) + V_GS ~ g.v - ifelse(d.v < s.v, d.v, s.v) + V_OV ~ V_GS - V_tn + + d.i ~ ifelse(d.v < s.v, -1, 1) * ifelse(V_GS < V_tn, + 0 + V_DS / R_DS, + ifelse(V_DS < V_OV, + ifelse(use_channel_length_modulation, + k_n * (1 + lambda * V_DS) * (V_OV - V_DS / 2) * V_DS + V_DS / R_DS, + k_n * (V_OV - V_DS / 2) * V_DS + V_DS / R_DS), + ifelse(use_channel_length_modulation, + ((k_n * V_OV^2) / 2) * (1 + lambda * V_DS) + V_DS / R_DS, + (k_n * V_OV^2) / 2 + V_DS / R_DS) + ) + ) + + g.i ~ 0 + s.i ~ -d.i + end +end + + + +""" + PMOS(;name, V_tp, R_DS, lambda) + +Creates an N-type MOSFET transistor + + # Structural Parameters + - `use_transconductance`: If `true` the parameter `k_p` needs to be provided, and is used in the calculation of the current + through the transistor. Otherwise, `mu_n`, `C_ox`, `W`, and `L` need to be provided and are used to calculate the transconductance. + + - `use_channel_length_modulation`: If `true` the channel length modulation effect is taken in to account. In essence this gives + the drain-source current has a small dependency on the drains-source voltage in the saturation region of operation. + + # Connectors + - `d` Drain Pin + - `g` Gate Pin + - `s` Source Pin + + # Parameters + - `mu_p`: Electron mobility + - `C_ox`: Oxide capacitance (F/m^2) + - `W`: Channel width (m) + - `L`: Channel length + - `k_p`: MOSFET transconductance parameter + +Based on the MOSFET models in (Sedra, A. S., Smith, K. C., Carusone, T. C., & Gaudet, V. C. (2021). Microelectronic circuits (8th ed.). Oxford University Press.) +""" +@mtkmodel PMOS begin + @variables begin + V_GS(t) + V_DS(t) + end + + @components begin + d = Pin(), [description = "Drain pin"] + g = Pin(), [description = "Gate pin"] + s = Pin(), [description = "Source pin"] + end + + @parameters begin + V_tp = -1.5, [description = "Threshold voltage (V)"] + R_DS = 1e7, [description = "Drain-source resistance (Ω)"] + + if use_channel_length_modulation + lambda = 1 / 25, [description = "Channel length modulation coefficient (V^(-1))"] + end + + if !use_transconductance + mu_p, [description = "Hole mobility"] + C_ox, [description = "Oxide capacitance (F/m^2)"] + W, [description = "Channel width (m)"] + L, [description = "Channel length (m)"] + else + k_p = 20e-3 + end + end + + @structural_parameters begin + use_transconductance = true + use_channel_length_modulation = true + end + + begin + if !use_transconductance + k_p = mu_p * C_ox * (W / L) + end + end + + @equations begin + V_DS ~ ifelse(d.v > s.v, s.v - d.v, d.v - s.v) + V_GS ~ g.v - ifelse(d.v > s.v, d.v, s.v) + + d.i ~ -ifelse(d.v > s.v, -1.0, 1.0) * ifelse(V_GS > V_tp, + 0.0 + V_DS / R_DS, + ifelse(V_DS > (V_GS - V_tp), + ifelse(use_channel_length_modulation, + k_p * (1 + lambda * V_DS) * ((V_GS - V_tp) - V_DS / 2) * V_DS + + V_DS / R_DS, + k_p * ((V_GS - V_tp) - V_DS / 2) * V_DS + V_DS / R_DS), + ifelse(use_channel_length_modulation, + ((k_p * (V_GS - V_tp)^2) / 2) * (1 + lambda * V_DS) + V_DS / R_DS, + (k_p * (V_GS - V_tp)^2) / 2 + V_DS / R_DS) + ) + ) + + g.i ~ 0 + s.i ~ -d.i + end +end \ No newline at end of file From d352e19860befa8cce3e5887d435241e09020083 Mon Sep 17 00:00:00 2001 From: jClugstor Date: Fri, 28 Mar 2025 11:46:20 -0400 Subject: [PATCH 02/14] add MOSFET tests --- test/Electrical/analog.jl | 207 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 207 insertions(+) diff --git a/test/Electrical/analog.jl b/test/Electrical/analog.jl index 8a9c7937..ec8f45dd 100644 --- a/test/Electrical/analog.jl +++ b/test/Electrical/analog.jl @@ -540,6 +540,213 @@ end # title = "RC Circuit Test with VariableResistor") # savefig(plt, "rc_circuit_test_variable_resistor") end +@testset "NMOS Transistor" begin + + @mtkmodel SimpleNMOSCircuit begin + @components begin + Q1 = NMOS() + Vcc = Voltage() + Vb = Voltage() + ground = Ground() + + Vcc_const = Constant(k=V_cc) + Vb_const = Constant(k=V_b) + end + + @parameters begin + V_cc = 5.0 + V_b = 3.5 + end + @equations begin + #voltage sources + connect(Vcc_const.output, Vcc.V) + connect(Vb_const.output, Vb.V) + + #ground connections + connect(Vcc.n, Vb.n, ground.g, Q1.s) + + #other stuff + connect(Vcc.p, Q1.d) + connect(Vb.p, Q1.g) + end + end + + @mtkbuild sys = SimpleNMOSCircuit(V_cc = 5.0, V_b = 3.5) + + prob = ODEProblem(sys, Pair[], (0.0, 10.0)) + sol = solve(prob) + @test sol[sys.Q1.d.i][1] ≈ 0.0874 + @test sol[sys.Q1.s.i][1] ≈ -0.0874 + @test sol[sys.Q1.g.i][1] == 0.0 + @test sol[sys.Q1.d.v][1] == 5.0 + @test sol[sys.Q1.s.v] < sol[sys.Q1.d.v] + + # test device symmetry + @mtkmodel FlippedNMOSCircuit begin + @components begin + Q1 = NMOS() + Vcc = Voltage() + Vb = Voltage() + ground = Ground() + + Vcc_const = Constant(k=V_cc) + Vb_const = Constant(k=V_b) + end + + @parameters begin + V_cc = 5.0 + V_b = 3.5 + end + @equations begin + #voltage sources + connect(Vcc_const.output, Vcc.V) + connect(Vb_const.output, Vb.V) + + #ground connections + connect(Vcc.n, Vb.n, ground.g, Q1.d) + + #other stuff + connect(Vcc.p, Q1.s) + connect(Vb.p, Q1.g) + end + end + + @mtkbuild flipped_sys = FlippedNMOSCircuit(V_cc=5.0, V_b=3.5) + + flipped_prob = ODEProblem(flipped_sys, Pair[], (0.0, 10.0)) + flipped_sol = solve(flipped_prob) + @test flipped_sol[flipped_sys.Q1.d.i][1] ≈ -0.0874 + @test flipped_sol[flipped_sys.Q1.s.i][1] ≈ 0.0874 + @test flipped_sol[flipped_sys.Q1.s.v] > flipped_sol[flipped_sys.Q1.d.v] + + # channel length modulation + @mtkmodel SimpleNMOSCircuit begin + @components begin + Q1 = NMOS(use_channel_length_modulation = false) + Vcc = Voltage() + Vb = Voltage() + ground = Ground() + + Vcc_const = Constant(k=V_cc) + Vb_const = Constant(k=V_b) + end + + @parameters begin + V_cc = 5.0 + V_b = 3.5 + end + @equations begin + #voltage sources + connect(Vcc_const.output, Vcc.V) + connect(Vb_const.output, Vb.V) + + #ground connections + connect(Vcc.n, Vb.n, ground.g, Q1.s) + + #other stuff + connect(Vcc.p, Q1.d) + connect(Vb.p, Q1.g) + end + end + + @mtkbuild sys = SimpleNMOSCircuit(V_cc = 5.0, V_b = 3.5) + + prob = ODEProblem(sys, Pair[], (0.0, 10.0)) + sol = solve(prob) + @test sol[sys.Q1.d.i][1] ≈ 0.0729 + @test sol[sys.Q1.s.i][1] ≈ -0.0729 +end + + +@testset "PMOS Transistor" begin + + @mtkmodel SimplePMOSCircuit begin + @components begin + Q1 = SimplePMOS(use_channel_length_modulation = false) + Vs = Voltage() + Vb = Voltage() + Vd = Voltage() + ground = Ground() + + Vs_const = Constant(k=V_s) + Vb_const = Constant(k=V_b) + Vd_const = Constant(k=V_d) + end + + @parameters begin + V_s = 5.0 + V_b = 3.5 + V_d = 0.0 + end + @equations begin + #voltage sources + connect(Vs_const.output, Vs.V) + connect(Vb_const.output, Vb.V) + connect(Vd_const.output, Vd.V ) + + #ground connections + connect(Vs.n, Vb.n, ground.g, Vd.n) + + connect(Vd.p, Q1.d) + #other stuff + connect(Vs.p, Q1.s) + connect(Vb.p, Q1.g) + end + end + + @mtkbuild sys = SimplePMOSCircuit(V_s=5.0, V_b=2.5, V_d = 3) + + prob = ODEProblem(sys, Pair[], (0.0, 10.0)) + sol = solve(prob) + + @test sol[sys.Q1.d.i][1] ≈ -0.0091998 + @test sol[sys.Q1.s.i][1] ≈ 0.0091998 + + # device symmetry + @mtkmodel FlippedPMOSCircuit begin + @components begin + Q1 = PMOS() + Vs = Voltage() + Vb = Voltage() + Vd = Voltage() + ground = Ground() + + Vs_const = Constant(k=V_s) + Vb_const = Constant(k=V_b) + Vd_const = Constant(k=V_d) + end + + @parameters begin + V_s = 5.0 + V_b = 3.5 + V_d = 0.0 + end + @equations begin + #voltage sources + connect(Vs_const.output, Vs.V) + connect(Vb_const.output, Vb.V) + connect(Vd_const.output, Vd.V ) + + #ground connections + connect(Vs.n, Vb.n, ground.g, Vd.n) + + connect(Vd.p, Q1.s) + #other stuff + connect(Vs.p, Q1.d) + connect(Vb.p, Q1.g) + end + end + + @mtkbuild flipped_sys = FlippedPMOSCircuit(V_s=5.0, V_b=2.5, V_d = 3) + + flipped_prob = ODEProblem(flipped_sys, Pair[], (0.0, 10.0)) + flipped_sol = solve(flipped_prob) + + flipped_sol[flipped_sys.Q1.d.i][1] ≈ 0.0091998 + flipped_sol[flipped_sys.Q1.s.i][1] ≈ -0.0091998 + + +end @testset "NPN Tests" begin @mtkmodel SimpleNPNCircuit begin From a295de3b363865cabab2dc2972d5fb9cd7db9418 Mon Sep 17 00:00:00 2001 From: jClugstor Date: Fri, 28 Mar 2025 16:42:26 -0400 Subject: [PATCH 03/14] add a little tutorial page --- docs/src/tutorials/MOSFET_calibration.md | 73 ++++++++++++++++++++++++ 1 file changed, 73 insertions(+) create mode 100644 docs/src/tutorials/MOSFET_calibration.md diff --git a/docs/src/tutorials/MOSFET_calibration.md b/docs/src/tutorials/MOSFET_calibration.md new file mode 100644 index 00000000..8ae9c110 --- /dev/null +++ b/docs/src/tutorials/MOSFET_calibration.md @@ -0,0 +1,73 @@ +# MOSFET I-V Curves +In this example, first we'll demonstrate the I-V curves of the NMOS transistor model. +First of all, we construct a circuit using the NMOS transistor. We'll need to import ModelingToolkit and the Electrical standard library that holds the transistor models. + +```@example NMOS +using ModelingToolkit +using ModelingToolkit: t_nounits as t +using ModelingToolkitStandardLibrary.Electrical +using OrdinaryDiffEq +using Plots +``` + +Here we just connect the source pin to ground, the drain pin to a voltage source named `Vcc`, and the gate pin to a voltage source named `Vb`. +```@example NMOS +@mtkmodel SimpleNMOSCircuit begin + @components begin + Q1 = NMOS() + Vcc = Voltage() + Vb = Voltage() + ground = Ground() + + Vcc_const = Constant(k=V_cc) + Vb_const = Constant(k=V_b) + end + + @parameters begin + V_cc = 5.0 + V_b = 3.5 + end + @equations begin + #voltage sources + connect(Vcc_const.output, Vcc.V) + connect(Vb_const.output, Vb.V) + + #ground connections + connect(Vcc.n, Vb.n, ground.g, Q1.s) + + #other stuff + connect(Vcc.p, Q1.d) + connect(Vb.p, Q1.g) + end +end + +@mtkbuild sys = SimpleNMOSCircuit(V_cc = 5.0, V_b = 3.5) + +prob = ODEProblem(sys, Pair[], (0.0, 10.0)) +sol = solve(prob) +``` + +Now to make sure that the transistor model is working like it's supposed to, we can examine the plots of the drain-source voltage vs. the drain current, otherwise knowns as the I-V curve of the transistor. +```@example NMOS +v_cc_list = collect(0.05:0.1:10.0) + +I_D_list = [] +I_D_lists = [] + +for V_b in [1.0, 1.4, 1.8, 2.2, 2.6] + I_D_list = [] + for V_cc in v_cc_list + @mtkbuild sys = SimpleNMOSCircuit(V_cc=V_cc, V_b=V_b) + prob = ODEProblem(sys, Pair[], (0.0, 10.0)) + sol = solve(prob) + push!(I_D_list, sol[sys.Q1.d.i][1]) + end + push!(I_D_lists, I_D_list) +end + +reduce(hcat, I_D_lists) +plot(v_cc_list, I_D_lists, title="NMOS IV Curves", label=["V_GS: 1.0 V" "V_GS: 1.4 V" "V_GS: 1.8 V" "V_GS: 2.2 V" "V_GS: 2.6 V"], xlabel = "Drain-Source Voltage (V)", ylabel = "Drain Current (A)") +``` + +We can see that we get exactly what we would expect: as the drain-source voltage increases, the drain current increases, until the the transistor gets in to the saturation region of operation. +Then the only increase in drain current is due to the channel-length modulation effect. Additionally, we can see that the maximum current reached increases as the gate voltage increases. \ No newline at end of file From 1a2174cdc6aa736aed39dc051fc44db490becda2 Mon Sep 17 00:00:00 2001 From: jClugstor Date: Thu, 10 Apr 2025 18:55:45 -0400 Subject: [PATCH 04/14] add docstrings to docspage --- docs/src/API/electrical.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/src/API/electrical.md b/docs/src/API/electrical.md index cc9dc2b3..788796ab 100644 --- a/docs/src/API/electrical.md +++ b/docs/src/API/electrical.md @@ -34,6 +34,8 @@ IdealOpAmp Diode HeatingDiode VariableResistor +NMOS +PMOS PNP NPN ``` From 0480c6626f9f9c4558dd58a414ab44f409ca0aa7 Mon Sep 17 00:00:00 2001 From: jClugstor Date: Fri, 11 Apr 2025 12:37:06 -0400 Subject: [PATCH 05/14] no component descriptions --- src/Electrical/Analog/mosfets.jl | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Electrical/Analog/mosfets.jl b/src/Electrical/Analog/mosfets.jl index 1b48b8d5..bd291775 100644 --- a/src/Electrical/Analog/mosfets.jl +++ b/src/Electrical/Analog/mosfets.jl @@ -32,9 +32,9 @@ Based on the MOSFET models in (Sedra, A. S., Smith, K. C., Carusone, T. C., & Ga end @components begin - d = Pin(), [description = "Drain pin"] - g = Pin(), [description = "Gate pin"] - s = Pin(), [description = "Source pin"] + d = Pin() + g = Pin() + s = Pin() end @parameters begin @@ -123,9 +123,9 @@ Based on the MOSFET models in (Sedra, A. S., Smith, K. C., Carusone, T. C., & Ga end @components begin - d = Pin(), [description = "Drain pin"] - g = Pin(), [description = "Gate pin"] - s = Pin(), [description = "Source pin"] + d = Pin() + g = Pin() + s = Pin() end @parameters begin From 773c35cd2614cc4b289d9a7f2aab35e91252837c Mon Sep 17 00:00:00 2001 From: jClugstor Date: Fri, 11 Apr 2025 14:02:07 -0400 Subject: [PATCH 06/14] fix testset typo --- test/Electrical/analog.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/Electrical/analog.jl b/test/Electrical/analog.jl index ec8f45dd..ceda50b2 100644 --- a/test/Electrical/analog.jl +++ b/test/Electrical/analog.jl @@ -658,7 +658,7 @@ end end -@testset "PMOS Transistor" begin +@testset "PMOS Transistor" begin begin @mtkmodel SimplePMOSCircuit begin @components begin From e08f6e4ab80df94db31d02eba19871303508d464 Mon Sep 17 00:00:00 2001 From: jClugstor Date: Fri, 11 Apr 2025 14:15:24 -0400 Subject: [PATCH 07/14] include in electrical library --- src/Electrical/Electrical.jl | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Electrical/Electrical.jl b/src/Electrical/Electrical.jl index baac5138..712e4b17 100644 --- a/src/Electrical/Electrical.jl +++ b/src/Electrical/Electrical.jl @@ -24,6 +24,9 @@ include("Analog/sensors.jl") export Voltage, Current include("Analog/sources.jl") +export NMOS, PMOS +include("Analog/mosfets.jl") + export NPN, PNP include("Analog/transistors.jl") From 862e071e707dab49428f40d90f70bc4b6e8a8d55 Mon Sep 17 00:00:00 2001 From: jClugstor Date: Fri, 11 Apr 2025 14:48:30 -0400 Subject: [PATCH 08/14] bring in Blocks.Constant --- docs/src/tutorials/MOSFET_calibration.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/src/tutorials/MOSFET_calibration.md b/docs/src/tutorials/MOSFET_calibration.md index 8ae9c110..ee7cab05 100644 --- a/docs/src/tutorials/MOSFET_calibration.md +++ b/docs/src/tutorials/MOSFET_calibration.md @@ -6,6 +6,7 @@ First of all, we construct a circuit using the NMOS transistor. We'll need to im using ModelingToolkit using ModelingToolkit: t_nounits as t using ModelingToolkitStandardLibrary.Electrical +using ModelingToolkitStandardLibrary.Blocks: Constant using OrdinaryDiffEq using Plots ``` From e3b01af545452fb7ca7f16f48d4f06d38dfe6906 Mon Sep 17 00:00:00 2001 From: jClugstor Date: Fri, 11 Apr 2025 15:14:37 -0400 Subject: [PATCH 09/14] fix tests --- test/Electrical/analog.jl | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/test/Electrical/analog.jl b/test/Electrical/analog.jl index ceda50b2..e25ac5db 100644 --- a/test/Electrical/analog.jl +++ b/test/Electrical/analog.jl @@ -575,8 +575,8 @@ end prob = ODEProblem(sys, Pair[], (0.0, 10.0)) sol = solve(prob) - @test sol[sys.Q1.d.i][1] ≈ 0.0874 - @test sol[sys.Q1.s.i][1] ≈ -0.0874 + @test sol[sys.Q1.d.i][1] > 0.0 + @test sol[sys.Q1.s.i][1] < 0.0 @test sol[sys.Q1.g.i][1] == 0.0 @test sol[sys.Q1.d.v][1] == 5.0 @test sol[sys.Q1.s.v] < sol[sys.Q1.d.v] @@ -615,8 +615,8 @@ end flipped_prob = ODEProblem(flipped_sys, Pair[], (0.0, 10.0)) flipped_sol = solve(flipped_prob) - @test flipped_sol[flipped_sys.Q1.d.i][1] ≈ -0.0874 - @test flipped_sol[flipped_sys.Q1.s.i][1] ≈ 0.0874 + @test flipped_sol[flipped_sys.Q1.d.i][1] < 0 + @test flipped_sol[flipped_sys.Q1.s.i][1] > 0 @test flipped_sol[flipped_sys.Q1.s.v] > flipped_sol[flipped_sys.Q1.d.v] # channel length modulation @@ -653,8 +653,8 @@ end prob = ODEProblem(sys, Pair[], (0.0, 10.0)) sol = solve(prob) - @test sol[sys.Q1.d.i][1] ≈ 0.0729 - @test sol[sys.Q1.s.i][1] ≈ -0.0729 + @test sol[sys.Q1.d.i][1] > 0.0 + @test sol[sys.Q1.s.i][1] < 0.0 end @@ -662,7 +662,7 @@ end @mtkmodel SimplePMOSCircuit begin @components begin - Q1 = SimplePMOS(use_channel_length_modulation = false) + Q1 = PMOS(use_channel_length_modulation = false) Vs = Voltage() Vb = Voltage() Vd = Voltage() @@ -699,8 +699,8 @@ end prob = ODEProblem(sys, Pair[], (0.0, 10.0)) sol = solve(prob) - @test sol[sys.Q1.d.i][1] ≈ -0.0091998 - @test sol[sys.Q1.s.i][1] ≈ 0.0091998 + @test sol[sys.Q1.d.i][1] < 0.0 + @test sol[sys.Q1.s.i][1] > 0.0 # device symmetry @mtkmodel FlippedPMOSCircuit begin @@ -742,8 +742,8 @@ end flipped_prob = ODEProblem(flipped_sys, Pair[], (0.0, 10.0)) flipped_sol = solve(flipped_prob) - flipped_sol[flipped_sys.Q1.d.i][1] ≈ 0.0091998 - flipped_sol[flipped_sys.Q1.s.i][1] ≈ -0.0091998 + flipped_sol[flipped_sys.Q1.d.i][1] > 0.0 + flipped_sol[flipped_sys.Q1.s.i][1] < 0.0 end From 632f00c1f296642589ad9a45688ee881575c9601 Mon Sep 17 00:00:00 2001 From: jClugstor Date: Fri, 11 Apr 2025 16:17:37 -0400 Subject: [PATCH 10/14] fix test maybe --- test/Electrical/analog.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/Electrical/analog.jl b/test/Electrical/analog.jl index e25ac5db..d66aad87 100644 --- a/test/Electrical/analog.jl +++ b/test/Electrical/analog.jl @@ -620,7 +620,7 @@ end @test flipped_sol[flipped_sys.Q1.s.v] > flipped_sol[flipped_sys.Q1.d.v] # channel length modulation - @mtkmodel SimpleNMOSCircuit begin + @mtkmodel SimpleNMOSCircuitChannel begin @components begin Q1 = NMOS(use_channel_length_modulation = false) Vcc = Voltage() @@ -649,7 +649,7 @@ end end end - @mtkbuild sys = SimpleNMOSCircuit(V_cc = 5.0, V_b = 3.5) + @mtkbuild sys = SimpleNMOSCircuitChannel(V_cc = 5.0, V_b = 3.5) prob = ODEProblem(sys, Pair[], (0.0, 10.0)) sol = solve(prob) From d800f28a7322df21780cbc1e8ad6b43f2273ba87 Mon Sep 17 00:00:00 2001 From: jClugstor Date: Mon, 14 Apr 2025 10:22:28 -0400 Subject: [PATCH 11/14] fix typo in tests --- test/Electrical/analog.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/Electrical/analog.jl b/test/Electrical/analog.jl index d66aad87..4aff45b3 100644 --- a/test/Electrical/analog.jl +++ b/test/Electrical/analog.jl @@ -658,7 +658,7 @@ end end -@testset "PMOS Transistor" begin begin +@testset "PMOS Transistor" begin @mtkmodel SimplePMOSCircuit begin @components begin From 95ea7cdf031e1ecc35fb8aae74a47be9ee7e4047 Mon Sep 17 00:00:00 2001 From: jClugstor Date: Sat, 7 Jun 2025 11:37:23 -0400 Subject: [PATCH 12/14] add D_nounits --- test/Electrical/analog.jl | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/test/Electrical/analog.jl b/test/Electrical/analog.jl index 4aff45b3..cc916e45 100644 --- a/test/Electrical/analog.jl +++ b/test/Electrical/analog.jl @@ -1,5 +1,5 @@ using ModelingToolkitStandardLibrary.Electrical, ModelingToolkit, OrdinaryDiffEq, Test -using ModelingToolkit: t_nounits as t +using ModelingToolkit: t_nounits as t, D_nounits as D using ModelingToolkitStandardLibrary.Blocks: Step, Constant, Sine, Cosine, ExpSine, Ramp, Square, Triangular @@ -646,15 +646,15 @@ end #other stuff connect(Vcc.p, Q1.d) connect(Vb.p, Q1.g) - end + end end - @mtkbuild sys = SimpleNMOSCircuitChannel(V_cc = 5.0, V_b = 3.5) + @mtkbuild sys = SimpleNMOSCircuitChannel(V_cc = 5.0, V_b = 3.5) - prob = ODEProblem(sys, Pair[], (0.0, 10.0)) - sol = solve(prob) - @test sol[sys.Q1.d.i][1] > 0.0 - @test sol[sys.Q1.s.i][1] < 0.0 + prob = ODEProblem(sys, Pair[], (0.0, 10.0)) + sol = solve(prob) + @test sol[sys.Q1.d.i][1] > 0.0 + @test sol[sys.Q1.s.i][1] < 0.0 end From 4367ecad7d558d9e0b3fef8fa7eb3a364a0171de Mon Sep 17 00:00:00 2001 From: jClugstor Date: Sat, 7 Jun 2025 14:34:56 -0400 Subject: [PATCH 13/14] get rid of use_channel_length modulation parameter --- src/Electrical/Analog/mosfets.jl | 26 ++++++-------------------- 1 file changed, 6 insertions(+), 20 deletions(-) diff --git a/src/Electrical/Analog/mosfets.jl b/src/Electrical/Analog/mosfets.jl index bd291775..42b1adcc 100644 --- a/src/Electrical/Analog/mosfets.jl +++ b/src/Electrical/Analog/mosfets.jl @@ -41,9 +41,7 @@ Based on the MOSFET models in (Sedra, A. S., Smith, K. C., Carusone, T. C., & Ga V_tn = 0.8, [description = "Threshold voltage (V)"] R_DS = 1e7, [description = "Drain to source resistance (Ω)"] - if use_channel_length_modulation - lambda = 1 / 25, [description = "Channel length modulation coefficient (V^(-1))"] - end + lambda = 0.04, [description = "Channel length modulation coefficient (V^(-1))"] if !use_transconductance mu_n, [description = "Electron mobility"] @@ -57,7 +55,6 @@ Based on the MOSFET models in (Sedra, A. S., Smith, K. C., Carusone, T. C., & Ga @structural_parameters begin use_transconductance = true - use_channel_length_modulation = true end begin @@ -72,16 +69,12 @@ Based on the MOSFET models in (Sedra, A. S., Smith, K. C., Carusone, T. C., & Ga V_OV ~ V_GS - V_tn d.i ~ ifelse(d.v < s.v, -1, 1) * ifelse(V_GS < V_tn, - 0 + V_DS / R_DS, + V_DS / R_DS, ifelse(V_DS < V_OV, - ifelse(use_channel_length_modulation, k_n * (1 + lambda * V_DS) * (V_OV - V_DS / 2) * V_DS + V_DS / R_DS, - k_n * (V_OV - V_DS / 2) * V_DS + V_DS / R_DS), - ifelse(use_channel_length_modulation, - ((k_n * V_OV^2) / 2) * (1 + lambda * V_DS) + V_DS / R_DS, - (k_n * V_OV^2) / 2 + V_DS / R_DS) + ((k_n * V_OV^2) / 2) * (1 + lambda * V_DS) + V_DS / R_DS + ) ) - ) g.i ~ 0 s.i ~ -d.i @@ -132,9 +125,7 @@ Based on the MOSFET models in (Sedra, A. S., Smith, K. C., Carusone, T. C., & Ga V_tp = -1.5, [description = "Threshold voltage (V)"] R_DS = 1e7, [description = "Drain-source resistance (Ω)"] - if use_channel_length_modulation - lambda = 1 / 25, [description = "Channel length modulation coefficient (V^(-1))"] - end + lambda = 1 / 25, [description = "Channel length modulation coefficient (V^(-1))"] if !use_transconductance mu_p, [description = "Hole mobility"] @@ -148,7 +139,6 @@ Based on the MOSFET models in (Sedra, A. S., Smith, K. C., Carusone, T. C., & Ga @structural_parameters begin use_transconductance = true - use_channel_length_modulation = true end begin @@ -162,15 +152,11 @@ Based on the MOSFET models in (Sedra, A. S., Smith, K. C., Carusone, T. C., & Ga V_GS ~ g.v - ifelse(d.v > s.v, d.v, s.v) d.i ~ -ifelse(d.v > s.v, -1.0, 1.0) * ifelse(V_GS > V_tp, - 0.0 + V_DS / R_DS, + V_DS / R_DS, ifelse(V_DS > (V_GS - V_tp), - ifelse(use_channel_length_modulation, k_p * (1 + lambda * V_DS) * ((V_GS - V_tp) - V_DS / 2) * V_DS + V_DS / R_DS, - k_p * ((V_GS - V_tp) - V_DS / 2) * V_DS + V_DS / R_DS), - ifelse(use_channel_length_modulation, ((k_p * (V_GS - V_tp)^2) / 2) * (1 + lambda * V_DS) + V_DS / R_DS, - (k_p * (V_GS - V_tp)^2) / 2 + V_DS / R_DS) ) ) From bd3446f7fea621f787f42e9ba0e061217c28d541 Mon Sep 17 00:00:00 2001 From: jClugstor Date: Sat, 7 Jun 2025 14:35:04 -0400 Subject: [PATCH 14/14] fix up tests --- test/Electrical/analog.jl | 44 ++++----------------------------------- 1 file changed, 4 insertions(+), 40 deletions(-) diff --git a/test/Electrical/analog.jl b/test/Electrical/analog.jl index cc916e45..c5658d89 100644 --- a/test/Electrical/analog.jl +++ b/test/Electrical/analog.jl @@ -618,43 +618,7 @@ end @test flipped_sol[flipped_sys.Q1.d.i][1] < 0 @test flipped_sol[flipped_sys.Q1.s.i][1] > 0 @test flipped_sol[flipped_sys.Q1.s.v] > flipped_sol[flipped_sys.Q1.d.v] - - # channel length modulation - @mtkmodel SimpleNMOSCircuitChannel begin - @components begin - Q1 = NMOS(use_channel_length_modulation = false) - Vcc = Voltage() - Vb = Voltage() - ground = Ground() - - Vcc_const = Constant(k=V_cc) - Vb_const = Constant(k=V_b) - end - - @parameters begin - V_cc = 5.0 - V_b = 3.5 - end - @equations begin - #voltage sources - connect(Vcc_const.output, Vcc.V) - connect(Vb_const.output, Vb.V) - - #ground connections - connect(Vcc.n, Vb.n, ground.g, Q1.s) - - #other stuff - connect(Vcc.p, Q1.d) - connect(Vb.p, Q1.g) - end - end - - @mtkbuild sys = SimpleNMOSCircuitChannel(V_cc = 5.0, V_b = 3.5) - - prob = ODEProblem(sys, Pair[], (0.0, 10.0)) - sol = solve(prob) - @test sol[sys.Q1.d.i][1] > 0.0 - @test sol[sys.Q1.s.i][1] < 0.0 + end @@ -662,7 +626,7 @@ end @mtkmodel SimplePMOSCircuit begin @components begin - Q1 = PMOS(use_channel_length_modulation = false) + Q1 = PMOS() Vs = Voltage() Vb = Voltage() Vd = Voltage() @@ -742,8 +706,8 @@ end flipped_prob = ODEProblem(flipped_sys, Pair[], (0.0, 10.0)) flipped_sol = solve(flipped_prob) - flipped_sol[flipped_sys.Q1.d.i][1] > 0.0 - flipped_sol[flipped_sys.Q1.s.i][1] < 0.0 + @test flipped_sol[flipped_sys.Q1.d.i][1] > 0.0 + @test flipped_sol[flipped_sys.Q1.s.i][1] < 0.0 end