Cascaded amplifiers¶

An amplifier with a single (real) pole is relatively easy to analyze and design with. This is partly why operational amplifiers are designed to have a dominantt-pole type of frequency response -- where the other poles and zeros are near or beyond the unity gain frequency.

The transfer function of cascaded amplifiers is simply the product of the individual transfer functions.

In [2]:
import numpy as np
import matplotlib.pyplot as plt
import matplotlib as mpl
from matplotlib.animation import FuncAnimation
from matplotlib import rcParams
from IPython.display import HTML

from matplotlib.ticker import EngFormatter

plt.style.use('seaborn-v0_8-poster')
rcParams['font.size'] = 18

#plt.ion()
In [3]:
fT = 10e6       # unity gain frequency
Av0 = 100e3     # DC gain
fH0 = fT / Av0  # 03dB frequency

f = np.logspace(1, 8, 1000)

def Amag(f, beta_inv, fH=fH0, Av0=Av0):
    """Return the magnitude of the transfer function of a single-pole
    amplifier having gain Av0 and a single pole at fH.
    
    beta_inv is the Beta**-1, or the gain of an ideal amplifier with
    negative feedback."""
    A = Av0 / (1 + 1j*f/fH0)
    Av = A / (1 + A/beta_inv)
    return abs(Av)
In [4]:
def nstages(G):
    fig, ax = plt.subplots(figsize=(10,7))
    ax.set(xlim=(min(f)/2, max(f)*2),
           ylim=(0.1, Av0),
           xlabel='Hz',
           ylabel='V / V',
           title='Frequency response of N stages for\nfixed system gain = %.0f' % G)
    
    ax.loglog(f, Amag(f, 1e6), '-k')
    ax.hlines(1, min(f), max(f), linewidth=1)

    ax.grid(True, linewidth=0.8)

    formatter1 = EngFormatter(places=0)
    ax.xaxis.set_major_formatter(formatter1)
    ax.yaxis.set_major_formatter(formatter1)

    # first line
    line = ax.plot(f, Amag(f, G), color='b', lw=2)[0]
    N_text = ax.text(2*fT/G, 2*G, 'N = 1', size='xx-large')
    g_text = ax.text(0.01*fT/G, 0.7*G, 'g = %i'%G, size='large', va='top')

    def amp(frame):
        n = frame + 1
        g = G**(1.0/n)
        a = Amag(f, g)**n
        line.set_ydata(a)
        N_text.set_text('N = %i' % n)
        g_text.set_text('g = %.1f' % g)
        return (line,)

    anim = FuncAnimation(fig, amp, interval=500, frames=7, repeat=True)
    return anim
In [5]:
HTML(nstages(10).to_html5_video())
Out[5]:
Your browser does not support the video tag.
No description has been provided for this image
In [6]:
HTML(nstages(100).to_html5_video())
Out[6]:
Your browser does not support the video tag.
No description has been provided for this image
In [7]:
HTML(nstages(1000).to_html5_video())
Out[7]:
Your browser does not support the video tag.
No description has been provided for this image
In [8]:
HTML(nstages(10000).to_html5_video())
Out[8]:
Your browser does not support the video tag.
No description has been provided for this image
In [9]:
# -3dB frequency vs. N stages for fixed system gain A and individual stage GBW

def f3(N, A, fT):
    g = A**(1.0/N)
    return (fT/g) * np.sqrt(2**(1.0/N)-1)

n = np.arange(1, 16)

fT = 10e6

def plot_bw(A):
    bw = f3(n, A, fT)/(fT/A)
    
    idx = bw.argmax()
    bmax = bw[idx]
    

    plt.close('all')
    fig, ax = plt.subplots()

    ax.plot(n, bw, 'o')
    ax.set(xticks=n, xlim=(0, n[-1]+1))

    yt = np.around(bw, 2)
    idxticks = [i for i in range(idx+1) if i<5 or i==idx]
    ax.set(yticks=bw[(0, 1, 2, 3, 4, 5, 8),])
    ax.set(yticks=bw[idxticks])


    formatter1 = EngFormatter(places=2)
    ax.yaxis.set_major_formatter(formatter1)

    ax.plot((0, idx+1), (bw[idx], bw[idx]), 'k', linewidth=1)

    ax.set(
        title='Bandwidth increase using N stages\nwith system gain = %.0f' % A,
        xlabel='N stages',
        ylabel='$BW_N \, / \, BW_1$')

    label = f"{idx+1}"
    ax.text(idx+1, (f3(idx, A, fT)*A/fT * 0.98), label, va='top', ha='center')

    plt.show()
    
<>:38: SyntaxWarning: invalid escape sequence '\,'
<>:38: SyntaxWarning: invalid escape sequence '\,'
/tmp/ipykernel_1606785/3923892182.py:38: SyntaxWarning: invalid escape sequence '\,'
  ylabel='$BW_N \, / \, BW_1$')
In [10]:
plot_bw(10)
No description has been provided for this image
In [11]:
plot_bw(100)
No description has been provided for this image
In [12]:
plot_bw(1000)
No description has been provided for this image
In [ ]:
 
In [ ]:
 
In [13]:
def plot_closedloop():
    fig, ax = plt.subplots(figsize=(10,7))
    ax.set(xlim=(min(f)/2, max(f)*2),
           ylim=(0.1, 2*Av0),
           ylabel='gain (V/V)',
           xlabel='Hz',
           title=r'Closed-loop gain and $1/\beta$ gain prediction')
    
    # open-loop amplifier, set 1/beta ~0
    ax.loglog(f, Amag(f, 1e9), '-k')
    ax.hlines(1, min(f), max(f), linewidth=1)

    ax.grid(True, linewidth=0.8)

    formatter1 = EngFormatter(places=0)
    ax.xaxis.set_major_formatter(formatter1)
    ax.yaxis.set_major_formatter(formatter1)
    
    # line and text
    bi = 1e9
    line_beta = ax.plot((f[0], f[-1]), [bi, bi], 'r')
    line_a = ax.plot(f, Amag(f, bi), color='b', lw=2)[0]
    
    A_text = ax.text(0.5*fT/bi, 0.7*Amag(0.5*fT/bi, bi), '$|A|$', size='large', color='b', va='top')
    beta_text = ax.text(7*fT/bi, 0.7*bi, r'$1 / \beta', size='large', color='r', va='top')

    bis = [1, 2, 5, 10, 20, 50, 100, 200, 500, 1e3, 2e3, 5e3, 10e3, 20e3, 50e3, 100e3]
    bis = [1, 10, 100, 1e3, 3e3, 10e3, 20e3, 50e3, 80e3, 100e3]
    #bis = np.logspace(0, np.log10(Av0), 20)
    bis = np.concatenate((bis, np.flip(bis,0)))

    def amp(frame):
        bi = bis[frame]
        a = Amag(f, bi)
        line_beta[0].set_ydata([bi, bi])
        line_a.set_ydata(a)
        
        A_text.set_position((0.5*fT/bi, 0.7*Amag(0.5*fT/bi, bi)))
        beta_text.set_position((7*fT/bi, 0.7*bi))
        beta_text.set_text(r'$1 / \beta$')
        
    
        return (line_a,)

    anim = FuncAnimation(fig, amp, interval=500, frames=len(bis), repeat=True, blit=True)
    return anim

HTML(plot_closedloop().to_html5_video())
Out[13]:
Your browser does not support the video tag.
No description has been provided for this image
In [14]:
G = 1000

fig, ax = plt.subplots(figsize=(10,7))
ax.set(xlim=(min(f)/2, max(f)*2),
       ylim=(0.1, Av0),
       xlabel='Hz',
       ylabel='V / V',
       title='Frequency response of N stages for\nfixed system gain = %.0f' % G)

ax.loglog(f, Amag(f, 1e6), '-k')
ax.hlines(1, min(f), max(f), linewidth=1)

ax.grid(True, linewidth=0.8)

formatter1 = EngFormatter(places=0)
ax.xaxis.set_major_formatter(formatter1)
ax.yaxis.set_major_formatter(formatter1)

# first line
line = ax.plot(f, Amag(f, G), color='b', lw=2)[0]
#N_text = ax.text(2*fT/G, 2*G, 'N = 1', size='xx-large')
#g_text = ax.text(0.01*fT/G, 0.7*G, 'g = %i'%G, size='large', va='top')

def amp(frame):
    n = frame + 1
    g = G**(1.0/n)
    a = Amag(f, g)**n
    ax.plot(f, a, lw=2)
    #N_text.set_text('N = %i' % n)
    #g_text.set_text('g = %.1f' % g)
    #return (line,)

for i in range(4):
    amp(i)
No description has been provided for this image
In [ ]: