Skip to content

Commit 3832cca

Browse files
martinholtersLasse Köper
andauthored
Extend MOSFET model (#21)
Include channel length modulation and allow `vt` and `α` to vary with the gate-source voltage by specifying polynomial coefficients. Co-authored-by: Lasse Köper <lasse.koeper@tuhh.de>
1 parent 41641a5 commit 3832cca

File tree

5 files changed

+94
-28
lines changed

5 files changed

+94
-28
lines changed

Project.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ uuid = "ca8b7239-ccd3-5cce-807f-2072f3f0d108"
33
version = "0.9.3-DEV"
44

55
[deps]
6+
Compat = "34da2185-b29b-5c13-b0c7-acf172513d20"
67
IterTools = "c8e1da08-722c-5040-9ed9-7db0dc04731e"
78
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
89
Markdown = "d6f4376e-aef5-505a-96c1-9c027394607a"
@@ -13,6 +14,7 @@ StaticArrays = "90137ffa-7385-5640-81b9-e52037218182"
1314
Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2"
1415

1516
[compat]
17+
Compat = "3.7"
1618
IterTools = "1"
1719
OrderedCollections = "1"
1820
ProgressMeter = "0.6, 0.7, 0.8, 0.9, 1"

docs/Manifest.toml

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,25 @@
11
[[ACME]]
2-
deps = ["IterTools", "LinearAlgebra", "Markdown", "OrderedCollections", "ProgressMeter", "SparseArrays", "StaticArrays", "Statistics"]
2+
deps = ["Compat", "IterTools", "LinearAlgebra", "Markdown", "OrderedCollections", "ProgressMeter", "SparseArrays", "StaticArrays", "Statistics"]
33
path = ".."
44
uuid = "ca8b7239-ccd3-5cce-807f-2072f3f0d108"
55

66
[[Base64]]
77
uuid = "2a0f44e3-6c83-55bd-87e4-b1978d98bd5f"
88

9+
[[Compat]]
10+
deps = ["Base64", "Dates", "DelimitedFiles", "Distributed", "InteractiveUtils", "LibGit2", "Libdl", "LinearAlgebra", "Markdown", "Mmap", "Pkg", "Printf", "REPL", "Random", "SHA", "Serialization", "SharedArrays", "Sockets", "SparseArrays", "Statistics", "Test", "UUIDs", "Unicode"]
11+
git-tree-sha1 = "2e23d71ad695ec28ca58ddd44869f07afa33cc76"
12+
uuid = "34da2185-b29b-5c13-b0c7-acf172513d20"
13+
version = "3.7.0"
14+
915
[[Dates]]
1016
deps = ["Printf"]
1117
uuid = "ade2ca70-3891-5945-98fb-dc099432e06a"
1218

19+
[[DelimitedFiles]]
20+
deps = ["Mmap"]
21+
uuid = "8bb1440f-4735-579b-a4ab-409b98df4dab"
22+
1323
[[Distributed]]
1424
deps = ["LinearAlgebra", "Random", "Serialization", "Sockets"]
1525
uuid = "8ba89e20-285c-5b6f-9357-94700520ee1b"
@@ -101,6 +111,10 @@ uuid = "ea8e919c-243c-51af-8825-aaa63cd721ce"
101111
[[Serialization]]
102112
uuid = "9e88b42a-f829-5b0c-bbe9-9e923198166b"
103113

114+
[[SharedArrays]]
115+
deps = ["Distributed", "Mmap", "Random", "Serialization"]
116+
uuid = "1a1011a3-84de-559e-8e89-a11a2f7dc383"
117+
104118
[[Sockets]]
105119
uuid = "6462fe0b-24de-5631-8697-dd941f90decc"
106120

src/ACME.jl

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
1-
# Copyright 2015, 2016, 2017, 2018, 2019 Martin Holters
1+
# Copyright 2015, 2016, 2017, 2018, 2019, 2020 Martin Holters
22
# See accompanying license file.
33

44
module ACME
55

66
export DiscreteModel, run!, steadystate, steadystate!, linearize, ModelRunner
77

8+
using Compat: evalpoly
89
using SparseArrays: SparseMatrixCSC, blockdiag, dropzeros!, findnz,
910
nonzeros, sparse, spzeros
1011
using LinearAlgebra: BLAS, I, axpy!, lu, rmul!

src/elements.jl

Lines changed: 51 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Copyright 2015, 2016, 2017, 2018, 2019 Martin Holters
1+
# Copyright 2015, 2016, 2017, 2018, 2019, 2020 Martin Holters
22
# See accompanying license file.
33

44
export resistor, potentiometer, capacitor, inductor, transformer,
@@ -394,49 +394,75 @@ Pins: `base`, `emitter`, `collector`
394394
end
395395

396396
@doc doc"""
397-
mosfet(typ; vt=0.7, α=2e-5)
397+
mosfet(typ; vt=0.7, α=2e-5, λ=0)
398398
399399
Creates a MOSFET transistor with the simple model
400400
401401
$i_D=\begin{cases}
402402
0 & \text{if } v_{GS} \le v_T \\
403403
\alpha \cdot (v_{GS} - v_T - \tfrac{1}{2}v_{DS})\cdot v_{DS}
404+
\cdot (1 + \lambda v_{DS})
404405
& \text{if } v_{DS} \le v_{GS} - v_T \cap v_{GS} > v_T \\
405-
\frac{\alpha}{2} \cdot (v_{GS} - v_T)^2 & \text{otherwise.}
406+
\frac{\alpha}{2} \cdot (v_{GS} - v_T)^2 \cdot (1 + \lambda v_{DS})
407+
& \text{otherwise.}
406408
\end{cases}$
407409
408410
The `typ` parameter chooses between NMOS (`:n`) and PMOS (`:p`). The threshold
409-
voltage `vt` is given in Volt, `α` (in A/V²) in a constant depending on the
410-
physics and dimensions of the device.
411+
voltage `vt` is given in Volt, `α` (in A/V²) is a constant depending on the
412+
physics and dimensions of the device, and `λ` (in V⁻¹) controls the channel
413+
length modulation.
414+
415+
Optionally, it is possible to specify tuples of coefficients for `vt` and `α`.
416+
These will be used as polynomials in $v_{GS}$ to determine $v_T$ and $\alpha$,
417+
respectively. E.g. with `vt=(0.7, 0.1, 0.02)`, the $v_{GS}$-dpendent threshold
418+
voltage $v_T = 0.7 + 0.1\cdot v_{GS} + 0.02\cdot v_{GS}^2$ will be used.
411419
412420
Pins: `gate`, `source`, `drain`
413-
""" function mosfet(typ; vt=0.7, α=2e-5)
421+
""" function mosfet(typ; vt=0.7, α=2e-5, λ=0)
414422
if typ == :n
415423
polarity = 1
416424
elseif typ == :p
417425
polarity = -1
418426
else
419427
throw(ArgumentError("Unknown mosfet type $(typ), must be :n or :p"))
420428
end
421-
return Element(mv=[-1 0; 0 -1; 0 0; 0 0],
422-
mi=[0 0; 0 0; 0 -1; 1 0],
423-
mq=polarity*[1 0 0; 0 1 0; 0 0 1; 0 0 0],
424-
u0=polarity*[-vt; 0; 0; 0],
425-
ports=[:gate => :source, :drain => :source],
426-
nonlinear_eq = @inline function (q)
427-
vg, vds, id=q # vg = vgs-vt
428-
if vg <= 0
429-
res = @SVector [-id]
430-
J = @SMatrix [0.0 0.0 -1.0]
431-
elseif vds <= vg
432-
res = @SVector* (vg-0.5*vds)*vds - id]
433-
J = @SMatrix*vds α*(vg-vds) -1.0]
434-
else # 0 < vg < vds
435-
res = @SVector [(α/2) * vg^2 - id]
436-
J = @SMatrix*vg 0.0 -1.0]
437-
end
438-
return (res, J)
439-
end)
429+
vt = (vt...,)
430+
α =...,)
431+
dvt = vt[2:end] .* (1:length(vt)-1...,)
432+
= α[2:end] .* (1:length(α)-1...,)
433+
let polarity = polarity, α = α, vt = vt
434+
return Element(mv=[-1 0; 0 -1; 0 0; 0 0],
435+
mi=[0 0; 0 0; 0 -1; 1 0],
436+
mq=polarity*[1 0 0; 0 1 0; 0 0 1; 0 0 0],
437+
ports=[:gate => :source, :drain => :source],
438+
nonlinear_eq = @inline function (q)
439+
vgs, vds, id = q
440+
α´ = evalpoly(polarity*vgs, α)
441+
if !isempty(dα)
442+
dα´_dvgs = evalpoly(polarity*vgs, dα)
443+
else
444+
dα´_dvgs = 0
445+
end
446+
vt´ = evalpoly(polarity*vgs, vt)
447+
if !isempty(dvt)
448+
dvt´_dvgs = evalpoly(polarity*vgs, dvt)
449+
else
450+
dvt´_dvgs = 0
451+
end
452+
λ´ = vds 0 ? λ : zero(λ)
453+
if vgs <= vt´
454+
res = @SVector [-id]
455+
J = @SMatrix [0.0 0.0 -1.0]
456+
elseif vds <= vgs - vt´ # && vgs > vt´
457+
res = @SVector [α´ * (vgs-vt´-0.5*vds)*vds*(1+λ´*vds) - id]
458+
J = @SMatrix [α´*(1-dvt´_dvgs)*vds*(1+λ´*vds) + dα´_dvgs * (vgs-vt´-0.5*vds)*vds*(1+λ´*vds) α´*(vgs-vt´+vds*(2*λ´*(vgs-vt´-0.75*vds)-1)) -1.0]
459+
else # 0 < vgs - vt´ < vds
460+
res = @SVector [(α´/2) * (vgs-vt´)^2*(1+λ´*vds) - id]
461+
J = @SMatrix [α´*(vgs-vt´)*(1-dvt´_dvgs)*(1+λ´*vds) + dα´_dvgs/2 * (vgs-vt´)^2*(1+λ´*vds) λ´*α´/2*(vgs-vt´)^2 -1.0]
462+
end
463+
return (res, J)
464+
end)
465+
end
440466
end
441467

442468
@doc doc"""

test/runtests.jl

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
1-
# Copyright 2015, 2016, 2017, 2018, 2019 Martin Holters
1+
# Copyright 2015, 2016, 2017, 2018, 2019, 2020 Martin Holters
22
# See accompanying license file.
33

44
include("checklic.jl")
55

66
using ACME
7+
using Compat: evalpoly, only
78
using Test: @test, @test_broken, @test_logs, @test_throws, @testset
89
using FFTW: rfft
910
using ProgressMeter
@@ -518,6 +519,28 @@ end
518519
y = run!(model, pol*[0 1 2 2 2; 5 5 0.5 1 1.5])
519520
@test y == pol*[0 0 1e-4*(1-0.5/2)*0.5 1e-4*(1-1/2)*1 1e-4/2*1^2]
520521
end
522+
for (typ, pol) in ((:n, 1), (:p, -1)), α in (1e-4, (0.0205, -0.0017)),
523+
vt in (1, (1.2078, 0.3238), (-1.2454, -0.199, -0.0483))
524+
circ = @circuit begin
525+
vgs = voltagesource(), [-] == gnd
526+
vds = voltagesource(), [-] == gnd
527+
J = mosfet(typ, vt=vt, α=α, λ=0.05), [gate] == vgs[+], [drain] == vds[+]
528+
out = currentprobe(), [+] == J[source], [-] == gnd
529+
end
530+
model = DiscreteModel(circ, 1);
531+
for vgs in range(0, stop=5, length=10), vds in range(0, stop=5, length=10)
532+
y = only(run!(model, pol*hcat([vgs; vds])))
533+
α´ = evalpoly(pol*vgs, (α...,))
534+
vt´ = evalpoly(pol*vgs, (vt...,))
535+
if vgs vt´
536+
@test y == 0
537+
elseif vds vgs - vt´
538+
@test y pol * α´ * (vgs - vt´ - vds / 2) * vds * (1 + 0.05 * vds)
539+
else
540+
@test y pol * α´ / 2 * (vgs - vt´)^2 * (1 + 0.05 * vds)
541+
end
542+
end
543+
end
521544
end
522545

523546
@testset "op amp" begin

0 commit comments

Comments
 (0)