Skip to content
copperhead.sh
Get started

Examples / TI op amp handbook / Basic amplifiers

Inverting amplifier

SBOA092B page 54, The Inverting Amplifier: EI through RI into the summing point, RO from the output back to it, and the non-inverting input on ground.

E_O / E_I = -R_O / R_I
the schematic, drawn by copperhead from the circuit's netlist
The schematic, drawn by copperhead from the circuit's netlist

The schematic is drawn by copperhead’s drafting engine from this circuit’s netlist, with KiCad’s own library symbols, and it opens in KiCad as figure/inverting_amplifier.kicad_sch. The op amp is KiCad’s generic one, since the handbook’s are ideal, and each terminal is a test point named as the program names it. KiCad reads back from the sheet exactly the connections the circuit has; draw_figures.py refuses to write one that does not.

the interconnect view, fang's own projection
The interconnect view, fang's own projection

The interconnect view is fang’s own projection. It names the parts as the program does, so it reads against the code below.

The figure names the two resistors and gives them no values, so the program chooses them and records the choice as a decision (values): 10 kΩ in and 100 kΩ across, for a gain of -10. The claim is a parameter, a_v = -10, and one constraint holds it to the parts:

require(equals(self.a_v, negative(over(self.r_out.resistance, self.r_in.resistance))))

Change either resistor and the check fails until a_v agrees.

out/simulation.txt, from the two decks under out/spice/:

RunMeasuredClaimed
gain, operating point, E_I = 1 V-10-10 (a_v), holds
bandwidth, gain at 1 kHz1010, holds
bandwidth, -3 dB point906.9 kHznot a claim

The handbook’s algebra lets the open-loop gain go to infinity. The bench’s op amp has 120 dB of it and 10 MHz of gain-bandwidth, so the DC gain lands within a part in 10^5 of -10, and the closed loop runs out near 10 MHz / 11, the noise gain. That corner is the op amp’s, not a claim of the handbook.

Terminal window
fang check examples/ti_opamp_handbook/basic_amplifiers/inverting_amplifier/inverting_amplifier.py
python examples/regenerate.py ti_opamp_handbook/basic_amplifiers/inverting_amplifier # needs ngspice
examples/ti_opamp_handbook/basic_amplifiers/inverting_amplifier/inverting_amplifier.py
"""The inverting amplifier, SBOA092B page 54.
Show 13 more lines
E_O / E_I = -R_O / R_I
The handbook derives it by letting the open-loop gain go to infinity, so the
inverting input sits at ground and the current through R_I is the current
through R_O. The figure names the two resistors and gives them no values, so
`values` records the pair chosen here: 10 kOhm in and 100 kOhm across, a gain
of -10.
The bench drives E_I with 1 V and reads E_O, then sweeps the frequency to show
where the claim stops holding: a 10 MHz op amp closed for a noise gain of 11
has a bandwidth near 900 kHz, which the ideal algebra does not see.
"""
import sys
from pathlib import Path
# The handbook's shared parts and bench live in the folder above the sections.
sys.path.insert(0, str(Path(__file__).resolve().parents[2]))
from fang.lang import Parameter, System, kOhm, require
from fang.parts import Resistor
from fang.rationale import Chooses, Cites
from fang.simulation import ACSweep, OperatingPoint
from handbook import (
Bench,
Claim,
Ground,
OpAmp,
Run,
Terminal,
equals,
negative,
over,
ratio,
)
class InvertingAmplifier(System):
"""E_I through R_I into the summing point, R_O back from the output."""
figure = Cites(
"E_O / E_I = -R_O / R_I",
document="SBOA092B, Handbook of Operational Amplifier Applications",
locator="page 54, The Inverting Amplifier",
)
values = Chooses(
"What are R_I and R_O?",
selected="10 kOhm and 100 kOhm, for a gain of -10",
alternatives=[
{
"option": "leave them unknown",
"reason": "a gain nobody can compute is not a claim anything can check",
},
],
rationale=(
"the figure names the resistors and gives no values",
"a decade of gain keeps E_O far inside the swing for a 1 V drive",
),
)
a_v = Parameter("1", default=-10 * ratio, description="E_O / E_I")
e_in = Terminal()
e_out = Terminal()
r_in = Resistor(resistance=10 * kOhm)
r_out = Resistor(resistance=100 * kOhm)
amp = OpAmp()
ground = Ground()
def architecture(self):
self.e_in.probe >> self.r_in.p1
self.r_in.p2 >> self.amp.inverting.signal
self.amp.inverting.signal >> self.r_out.p1
self.r_out.p2 >> self.amp.output.signal
self.amp.output.signal >> self.e_out.probe
self.amp.non_inverting.signal >> self.ground.node
def constraints(self):
require(equals(self.a_v, negative(over(self.r_out.resistance, self.r_in.resistance))))
BENCH = Bench(
page=54,
title="The Inverting Amplifier",
runs=[
Run(
"gain",
OperatingPoint(),
drive={"e_in": "DC 1"},
measure={"gain": "v({e_out.1}) / v({e_in.1})"},
claims=[Claim("gain", "a_v", within=0.001)],
),
Run(
"bandwidth",
ACSweep(points=20, start="10", stop="10meg"),
drive={"e_in": "DC 0 AC 1"},
measure={
"gain_1k": "find vm({e_out.1}) at=1k",
"f_3db": "when vdb({e_out.1})=17 fall=1",
},
claims=[Claim("gain_1k", 10, within=0.001)],
units={"f_3db": "Hz"},
note=(
"The magnitude, so 10 rather than -10. The -3 dB point is "
"not a handbook claim: it is the op amp's, 10 MHz over a noise "
"gain of 11."
),
),
],
)

The parts, then the nets and the pads on them.

out/netlist.txt
GND1 Ground -
R1 10 kOhm -
R2 100 kOhm -
TP1 Terminal -
TP2 Terminal -
U1 OpAmp -
Net-(GND1-Pad1) GND1.1 U1.IN+
Net-(R1-Pad1) R1.1 TP1.1
Net-(R1-Pad2) R1.2 R2.1 U1.IN-
Net-(R2-Pad2) R2.2 TP2.1 U1.OUT

Every check that ran, and every one left undecided.

out/checks.txt
1 checks, 0 failed, 0 undecided

What the elaborated graph contains, by entity kind.

out/graph.txt
1 block
6 component
12 connection
1 constraint
1 decision
1 evidence
3 interface
10 pin
10 port
45 total
snapshot sha256:90fdbdd5e6dfc85266d9af0ab54257f27fb9e62b51c0542cb49361ecaeab75af

All of it, including the KiCad netlist, is in examples/ti_opamp_handbook/basic_amplifiers/inverting_amplifier/out/. Rebuild it with:

Terminal window
fang build examples/ti_opamp_handbook/basic_amplifiers/inverting_amplifier/inverting_amplifier.py