Category Archives: Silly scripts

A silly script for two-stage OP compensation

As a complement to one of my lectures on compensation of operational amplifiers, I wrote a crude MATLAB example for testing stability and ability to loop for example currents, voltages, etc. In some sense, this could be done using veriloga blocks and slighlty more accurate circuit descriptions. But yet — this is a quite good way of understanding the operation of something as simple as a two-pole system.

I’m referring to a standard two-stage amplifier with a differential-pair in the first stage and a common-source at the output. Attached is also a plot result from octave illustrating the pole/zero placement.

Octave plot of phase and amplitude

Octave plot of phase and amplitude

And you get some results in raw format:

octave:1> antikPoleZero
f_ug = 3.0917e+08
phi_m = 54.465
p_1 = -1.1171e+05
p_2 = -3.2733e+09
p_3 = -7.7695e+12
z_1 = 2.2957e+10

So, just an example/suggestion of simple ways to get some more understanding of the operation of the circuit.


%
% Mainly, this "demo" concentrates on the classical two-stage
% amplifier. This implies also that we have not employed the
% suggested technique by J. Baker, et al.
%
% Some of the design rules:

% Miller

% z_1 approx 10*w_ug => gm_II = 10*gm_I
% p_2 approx 2.2*w_ug => C_C = 0.22 * C_II
% or more generically => C_C = 2.2*C_II/(gm_II/gm_I)

% Nulling resistor:

% And then (nulling resistor option 2, where z_1 -> inf):
% p_2 approx 1.73*w_ug => C_C = 1.73 * C_II / (gm_II/gm_I)
% p_3 > 10 * w_ug
% R_Z = 1/gm_II

% Setting up the frequencies
% No need to touch this
% =========================================================
N = 256;
f = logspace(1, 11, N);
w = 2*pi*f;
s = j*w;

% Some "process"-dependent parameters. Assuming a relatively
% strong channel-length modulation.
lambda = 0.05;

% =========================================================

% =========================================================
% Do your changes here. Why not a for loop on top? You can
% characterize the phase margin as function of tail current,
% or similar.
% =========================================================
% =========================================================
% =========================================================

% Going to a more circuit-level representation:
% Tail current through the differential pair.
I_0 = 100e-6;

% Effective input voltage on diff-pair transistors:
Veff_I = 0.2;

% The second-stage driving capacitor. For sake of argument,
% the effective is slightly higher here.
Veff_II = 0.4;

% The mirror ratio between the output stage and the input
% stage, i.e., the output stage drives K times more current
% than the differential pair.
% Notice that the gm_II = gm_I * K * Veff_I / Veff_II;
K = 20;

% Internal stage capacitance. For sake of modeling, we have
% assumed that the drive transistor in the second stage also
% scales with K (given a constant current and Veff_II).
% C_I is approximately the CGS of the drive transistor.
% Assuming some fF-cap on the gate:

C_I = K*10e-15;

% C_II is the load capacitance.
C_II = 2e-12;

% Some tentative compensation network to start with.
C_C = 0.22 * C_II;
R_Z = 1 ; % AND ALSO SEE BELOW!

% =========================================================
% =========================================================
% =========================================================

% We can now form the different parameters, etc.
% Given the parameters above, calculate the transfer function:
% No need to touch this.
% =============================================================

% First stage:
gm_I = 2*I_0 / Veff_I;
g_I = lambda*I_0;
% Second stage:
gm_II = 2*K*I_0/Veff_II;
g_II = lambda*K*I_0;

A_I = gm_I / g_I;
A_II = gm_II / g_II;

a = A_I * A_II;
b = (C_II + C_C)/g_II + (C_I+C_C)/g_I + a*C_C/gm_I + R_Z*C_C;
c = ((1/(g_I*g_II))*(C_I*C_II + C_C*C_I + C_C*C_II) + ...
R_Z*C_C*(C_I/g_I + C_II/g_II));

d = R_Z*C_I*C_II*C_C/g_I/g_II;

z_1 = 1/(C_C/gm_II - R_Z*C_C);

% =========================================================
% =========================================================
% =========================================================
R_Z = 1 /gm_II;
% =========================================================
% =========================================================
% =========================================================

A_s = a * ( 1 - s /z_1) ./ ...
( 1 + b*s + c*s.^2 + d*s.^3);

% Derive the roots as:
p = roots([1 c/d b/d 1/d ]);

% Just for some pretty-printing.
p_1 = p(3);
p_2 = p(2);
p_3 = p(1);

% Amplitude and phase characteristics
log_A = 20*log10(abs(A_s));
ang_A = 180*unwrap(angle(A_s))/pi;

% Find the unity-gain frequency (in Hz) and phase margin

f_ug = mean(f(find(abs(log_A)==min(abs(log_A)))));
phi_m = mean(ang_A(find(abs(log_A)==min(abs(log_A)))))+180;

% the mean-thing is there to avoid some numerical issues.

fh = figure(1);
subplot(2,1,1);
sh(1) = semilogx(f, log_A);
hold on;
ph(1) = plot(abs(p_1/2/pi), 0, 'x');
ph(2) = plot(abs(p_2/2/pi), 0, 'x');
ph(3) = plot(abs(p_3/2/pi), 0, 'x');
ph(4) = plot(abs(z_1/2/pi), 0, 'o');
lh = line(f_ug*[1 1], [20*log10(abs(A_s(1))) -150]);
tl(1) = title('Amplitude characteristics');
tl(2) = ylabel('|A^2(j \omega )|');
hold off;

subplot(2,1,2);
sh(2) = semilogx(f, ang_A);
hold on;
ph(5) = plot(abs(p_1/2/pi), 0, 'x');
ph(6) = plot(abs(p_2/2/pi), 0, 'x');
ph(7) = plot(abs(p_3/2/pi), 0, 'x');
ph(8) = plot(abs(z_1/2/pi), 0, 'o');
lh = line(f_ug*[1 1], [0 -180]);
tl(3) = ylabel('arg{ A(j \omega )}');
tl(4) = xlabel('Frequency [Hz]');
hold off;

set(tl,'FontSize',18);
set(ph,'LineWidth',4);
set(sh,'LineWidth',4);

% =============================================================
% Dumping some data in raw format

f_ug
phi_m
p_1
p_2
p_3
z_1

% =============================================================

Two-stage OP and macro model

Two-stage OP and macro model


A silly script to manipulate Ooo properties variables

[[Thanks to Reza too!]]

I like the


File > Properties > Custom properties ...

option in Openoffice.org (and LibreOffice) where I can specify different parameters/variables in my documents. This could be revision numbers, repo indicators, and what have you.

Now, since the Ooo actually stores the documents in a zipped xml format, the variables can be accessed through the linux domain with for example:


#!/bin/sh
...
oooFile=/some/path/to/your/file/fileName.odt
KEY="Document number"
unzip -pa $oooFile meta.xml | tr -s "<" "\n" | grep "$KEY" | tr -s ">" "\n" | tail -n 1

(Yes, there is a flaw, since if there would be $KEY’s that are substrings of each other, it could fail, I’ll leave to the happy hacker to fix that and post here in that case. I’ll solve it by choosing my property names.).

The Ooo file is unzipped, but only the meta.xml is extracted. We then replace the “<" with carriage returns to split the XML tags from their contents. We also strip at the end of the value of the property.

Now, encapsulating this in a function, for example daisyGetVarOoo.sh is easy as a cake. And multiple variables can be extracted on another level, for example:


KEY="Document repo"
docRepo=`daisyGetVarOoo.sh $oooFile "$KEY"`
KEY="Document number"
docNo=`daisyGetVarOoo.sh $oooFile "$KEY"`
KEY="Document type"
docType=`daisyGetVarOoo.sh $oooFile "$KEY"`
KEY="Document tag"
docTag=`daisyGetVarOoo.sh $oooFile "$KEY"`
KEY="Document revision"
docRev=`daisyGetVarOoo.sh $oooFile "$KEY"`
echo "${docRepo}_${docNo}_${docType}_${docTag}_${docRev}"

Using the same approach, it is also easy to modify the existing property and replace the meta.xml file in the zipped archive. Through linux we can therefore create and change the variables. This is quite handy in a documention versioning system – or if you want to bulk change names or sanity check variables settings in large batch of files.


A Silly Script to Create OCEAN desVars

As we are now ramping up some of the projects for the TSTE16 course at our university, I wanted to just propose a small snippet to get your design variables from MATLAB into Cadence (or more exactly the OCEAN-script for simulating Spectre). The idea is to have MATLAB as master in these projects. This also implies that you want to set any design variables using MATLAB rather than hacking multiple files and risking loosing track of what you’ve been doing.

There are many ways to do this:
(1) One option is to use the simulink interface provided by MATLAB and Cadence, but is a bit over the top in this case.
(2) Another option is to create all the required netlists in the Cadence ADE environment. This will actually generate multiple files in your simulation directory. The “full” file which will be used for spectre is normally called “input.scs” . Further on, if you play that play button (Cadence 6) and study the log file, you actually see in the text which spectre command that is run. (Further on, this command is also stored in the runSimulation “script” stored by Cadence in that directory). The input.scs file is just a concatenation of several files, such as netlistHeader, netlist, netlistFooter — but also some dot-files, like .modelFiles and .designVariables. The latter is a text version of your variables that you could alter if you like. Then concatenate the files into the input.scs accordingly. And run. Requires some more hands on and have less post-processing options.

(3) Or assume we have already generated the Cadence test bench and generated an OCEAN script with a bunch of desVar definitions in it. We can replace those rows with a load command, e.g.

(loadi "tste16ProjDesVars.il")

which you have stored accessible somewhere.
Then make sure you can change that file with MATLAB. Like for example:


desVar(1).Name = 'vccr12';
desVar(1).Value = 1.2;

desVar(2).Name = 'vLo';
desVar(2).Value = 0.1;

desVar(3).Name = 'vHi';
desVar(3).Value = 1.1;

desVar(4).Name = 'compGain';
desVar(4).Value = 1000;

fClk = 1e6;
desVar(5).Name = 'fClk';
desVar(5).Value = fClk;

sigLength = 8;
desVar(6).Name = 'sigLength';
desVar(6).Value = sigLength;

desVar(7).Name = 'tStop';
desVar(7).Value = sigLength/fClk;

desVar(8).Name = 'daisyDemoTopReadFile';
desVar(8).Value = '/home/jacobw/TSTE16/testIn.txt';

desVar(9).Name = 'daisyDemoTopWriteFile';
desVar(9).Value = '/home/jacobw/TSTE16/testOut.txt';

%% This part would then be in a separate function.
fid = fopen('tste16ProjDesVar.il','w');
fprintf(fid, ';; ==================================== \n');
fprintf(fid, ';; Design variables generated by MATLAB \n');
fprintf(fid, ';; ==================================== \n');
for M = 1:length(desVar);
if isstr(desVar(M).Value)
fprintf(fid, '(desVar "%s" "\\"%s\\"")\n', desVar(M).Name, ...
desVar(M).Value);
else
fprintf(fid, '(desVar "%s" %f)\n', desVar(M).Name, ...
desVar(M).Value);
end
end;
fprintf(fid, ';; ==================================== \n');
close(fid);

Where I have used structs to store the intermediate design variables. You can also store strings into the design variables to pass on file names or similar to verilog A blocks in Cadence.
This gives you a file like


;; ====================================
;; Design variables generated by MATLAB
;; ====================================
(desVar "vccr12" 1.200000)
(desVar "vLo" 0.100000)
(desVar "vHi" 1.100000)
(desVar "compGain" 1000.000000)
(desVar "fClk" 1000000.000000)
(desVar "sigLength" 8.000000)
(desVar "tStop" 0.000008)
(desVar "daisyDemoTopReadFile" "\"/home/jacobw/TSTE16/testIn.txt\"")
(desVar "daisyDemoTopWriteFile" "\"/home/jacobw/TSTE16/testOut.txt\"")
;; ====================================

Happy hacking!


Follow

Get every new post delivered to your Inbox.

Join 45 other followers