// Author : Ozgur Cobanoglu, 5 May 2005, Torino // Ozgur.Cobanoglu@cern.ch, cobanogl@to.infn.it // // This simple code corresponds to my hand calculations for the // course of microelectronics lectured by Angelo Rivetti in 2005. // // This code provides some simple numerical methods for the problems // which have already been solved analitically. #include<math.h> #include<stdio.h> #include<stdlib.h> #include<string.h> #define separator printf("______________________________[Page:%d]\n", sepCou++); #define ERR printf("FATAL ERROR !..\n"); exit(1); #define db printf("---------\n"); #define dbdb printf("+++++++++\n"); bool FUNCTION_TRACING_ON = false; // set this to true whereas needed int sepCou=2; // the first page that has a question is 2 // global technological parameters gathered from page 1 double MNCOX = 200; // microA/Vsquare, uA/V^2 double MPCOX = 50; // microA/Vsquare, uA/V^2 double VTHN = 0.5; // volt double VTHP = -0.5; // volt double GAMMAN = 0.5; // sqrt(volt) double GAMMAP = 0.5; // sqrt(volt) double LAMBDAN = 0.02; // 1/volt double LAMBDAP = 0.02; // 1/volt double _2QF = 0.6; // volt double TOX = 5; // nm double COX = 5; // fF/microMeterSquare, fF/umV^2 double KN = MNCOX; // alias double KP = MPCOX; // alias //______________________________________________________________________________ double Vth(double vsb, const char* type="nmos") { double vth; if (!strcmp(type, "nmos")) { vth = VTHN+GAMMAN*(sqrt(fabs(_2QF+vsb))-sqrt(fabs(_2QF))); } else if (!strcmp(type, "pmos")) { vth = VTHP+GAMMAP*(sqrt(fabs(_2QF+vsb))-sqrt(fabs(_2QF))); } else { ERR } // function tracing if (FUNCTION_TRACING_ON) printf("## Function Vth ## Vsb:%f type:%s [Vth=%f]\n", vsb, type, vth); return vth; } //______________________________________________________________________________ double Ids(double VGS, double VDS, double W, double L, double vsb=0, const char* type="nmos") { double ids; char region[3]; double VTH = Vth(vsb, type); if (!strcmp(type, "nmos")) { // decide region if (VGS>VTH && VDS>VGS-VTH) { sprintf(region, "sat"); } else if (VGS>VTH && VDS<=VGS-VTH) { sprintf(region, "tri"); } else if (VGS<VTH) { sprintf(region, "off"); } else { ERR } } else if (!strcmp(type, "pmos")) { if (VGS<VTH && -VDS>-VGS-fabs(VTH)) { //if (VGS<VTH && -VDS>-VGS-VTH) { sprintf(region, "sat"); } else if (VGS<VTH && -VDS<=-VGS-fabs(VTH)) { //} else if (VGS<VTH && -VDS<=-VGS-VTH) { sprintf(region, "tri"); } else if (VGS>VTH) { sprintf(region, "off"); } else { ERR } } if (!strcmp(region, "sat")) { // calculate Ids if (!strcmp(type, "nmos")) { ids = 0.5*MNCOX*(W/L)*(VGS-VTH)*(VGS-VTH)*(1+LAMBDAN*VDS); } else if (!strcmp(type, "pmos")) { ids = -0.5*MPCOX*(W/L)*(fabs(VGS)-fabs(VTH))\ *(fabs(VGS)-fabs(VTH))*(1+LAMBDAP*fabs(VDS)); } else { ERR } } else if (!strcmp(region, "tri")) { if (!strcmp(type, "nmos")) { ids = MNCOX*(W/L)*((VGS-VTH)*VDS-0.5*VDS*VDS); } else if (!strcmp(type, "pmos")) { ids = -MPCOX*(W/L)*((VGS-fabs(VTH))*VDS-0.5*VDS*VDS); } else { ERR } } else if (!strcmp(region, "off")) { ids = 0; } else { ERR; } // function tracing if (FUNCTION_TRACING_ON) printf("## Function Ids ## Vsb:%f Vth:%f Vgs:%f Vds:%f W/L:%f Type:%s Region:%s [Ids=%f uA]\n", vsb, VTH, VGS, VDS, W/L, type, region, ids); return ids; } //______________________________________________________________________________ double gm_1(double W, double L, double Vgs, double Vds, double Vsb=0, const char* type="nmos") { // Razavi, page 21, eq.(2.17), channel length modulated double gm; double VTH = Vth(Vsb, type); if (!strcmp(type, "nmos")) { gm = MNCOX*(W/L)*(Vgs-VTH)*(1+LAMBDAN*Vds); } else if (!strcmp(type, "pmos")){ gm = MPCOX*(W/L)*(Vgs-VTH)*(1+LAMBDAP*Vds); } else { ERR } // function tracing if (FUNCTION_TRACING_ON) printf("## Function Gm_1 ## Vsb:%f \ Vth:%f Vgs:%f W/L:%f Type:%s [gm_1=%f uMho]\n", \ Vsb, VTH, Vgs, W/L, type, gm); return gm; } //______________________________________________________________________________ double gm_2(double Id, double W, double L, double Vds, const char* type="nmos") { // Razavi, page 21, eq.(2.18), channel length modulated double gm; if (!strcmp(type, "nmos")) { gm = sqrt((2*MNCOX*(W/L)*Id)/(1+LAMBDAN*Vds)); } else if (!strcmp(type, "pmos")) { gm = sqrt((2*MPCOX*(W/L)*Id)/(1+LAMBDAP*Vds)); } else { ERR } // function tracing if (FUNCTION_TRACING_ON) printf("## Function Gm_2 ## Id:%f \ W/L:%f Type:%s [gm_2=%f uMho]\n", Id,\ W/L, type, gm); return gm; } //______________________________________________________________________________ double gm_3(double Id, double Vgs, double Vsb=0, const char* type="nmos") { // Razavi, page 21, eq.(2.19) double gm; double VTH = Vth(Vsb, type); gm = (2*Id)/(Vgs-VTH); // function tracing if (FUNCTION_TRACING_ON) printf("## Function Gm_3 ## Id:%f \ Vgs:%f Vsb:%f Type:%s [gm_3=%f uMho]\n", \ Id, Vgs, Vsb, type, gm); return gm; // uMho or uS } //______________________________________________________________________________ double gm_triodeRegion(double Vds, double W, double L, double Vsb=0, const char* type="nmos") { // Razavi, page 22, eq.(2.21) double gm; double VTH = Vth(Vsb, type); if (!strcmp(type, "nmos")) { gm = MNCOX*(W/L)*Vds; } else if (!strcmp(type, "pmos")) { gm = MPCOX*(W/L)*Vds; } else { ERR } // function tracing if (FUNCTION_TRACING_ON) printf("## Function Gm_t ## Vds:%f Vsb:%f Type:%s [gm_t=%f uMho]\n", Vds, Vsb, type, gm); return gm; } //______________________________________________________________________________ double Vgs_sat(double Id, double W, double L, double Vds, double Vsb=0, const char* type="nmos") { // Derived from the clasic Id formule for saturation region double vgs, VTH = Vth(Vsb, type); if (!strcmp(type, "nmos")) { vgs = VTH+sqrt((2*Id*L)/(MNCOX*W*(1+LAMBDAN*Vds))); } else if (!strcmp(type, "pmos")) { vgs = -(fabs(VTH)+sqrt((-2*Id*L)/(MPCOX*W*(1+LAMBDAP*Vds)))); } else { ERR } // function tracing if (FUNCTION_TRACING_ON) printf("## Function Vgs_sat ## Id:%f W/L:%f/%f Vsb:%f Type:%s [Vgs_sat=%f]\n", Id, W, L, Vsb, type, vgs); return vgs; } //______________________________________________________________________________ double Vgs_tri(double Id, double W, double L, double Vds, double Vsb=0, const char* type="nmos") { // Derived from the clasic Id formulae for triode region double vgs, VTH = Vth(Vsb, type); if (!strcmp(type, "nmos")) { vgs = VTH+((Id*L)/(MNCOX*W)+Vds*Vds*0.5)/Vds; } else if (!strcmp(type, "pmos")) { vgs = -(fabs(VTH)+((Id*L)/(MPCOX*W)+Vds*Vds*0.5)/Vds); } else { ERR } // function tracing if (FUNCTION_TRACING_ON) printf("## Function Vgs_tri ## Id:%f Vds:%f W/L:%f/%f Vsb:%f Type:%s [Vgs_tri=%f]\n", Id, Vds, W, L, Vsb, type, vgs); return vgs; } //______________________________________________________________________________ double r0(double Id, const char* type="nmos") { // [Id]=uA double r; if (!strcmp(type, "nmos")) { r=1/(LAMBDAN*Id*0.000001); } else if (!strcmp(type, "pmos")) { r=1/(LAMBDAP*Id*0.000001); } else { ERR } // function tracing if (FUNCTION_TRACING_ON) printf("## Function r0 ## Id:%f type:%s [r0=%f MOhm]\n", Id, type, r/1000000); return r; // Ohm } //______________________________________________________________________________ double gmb(double gm, double Vsb, const char* type="nmos") { double g; if (!strcmp(type, "nmos")) { g=gm*(GAMMAN/(2*sqrt(_2QF+Vsb))); } else if (!strcmp(type, "pmos")) { g=gm*(GAMMAP/(2*sqrt(_2QF+Vsb))); } else { ERR } // function tracing if (FUNCTION_TRACING_ON) printf("## Function gmb ## gm:%f Vsb:%f type:%s [gmb=%f]\n", gm, Vsb, type, g); return g; } //______________________________________________________________________________ double Vds_sat(double Id, double W, double L, double Vgs, double Vsb=0, const char* type="nmos") { // Derved from the Id formula double v, VTH = Vth(Vsb, type); if (!strcmp(type, "nmos")) { v = ((2*Id*L)/(MNCOX*W*(Vgs-VTH)*(Vgs-VTH))-1)/LAMBDAN; } else if (!strcmp(type, "pmos")) { v = ((2*Id*L)/(MPCOX*W*(Vgs-VTH)*(Vgs-VTH))-1)/LAMBDAP; } else { ERR } // function tracing if (FUNCTION_TRACING_ON) printf("## Function Vds_sat ## Id:%f W/L:%f/%f Vgs:%f Vsb:%f Type:%s [Vds_sat=%f]\n", Id, W, L, Vgs, Vsb, type, v); return v; } //______________________________________________________________________________ double Vds_tri(double Id, double W, double L, double Vgs, double Vsb=0, const char* type="nmos") { // Derved from the Id formula double v, VTH = Vth(Vsb, type); if (!strcmp(type, "nmos")) { v = (Vgs-VTH)*(1+Vgs-VTH)-(2*Id*L)/(MNCOX*W); } else if (!strcmp(type, "pmos")) { v = (Vgs-VTH)*(1+Vgs-VTH)-(2*Id*L)/(MPCOX*W); } else { ERR } // function tracing if (FUNCTION_TRACING_ON) printf("## Function Vds_tri ## Id:%f W/L:%f/%f Vgs:%f Vsb:%f Type:%s [Vds_tri=%f]\n", Id, W, L, Vgs, Vsb, type, v); return v; } //______________________________________________________________________________ double Av_CS_sd(double gm, double Rd, double Rs) { // CS stage with source degeneration, page 62, eq. 3.56 // [gm]=uMho, [Rd, Rs]=Ohm return -Rd/(1000000/gm+Rs); } //______________________________________________________________________________ double Ron() { // Page 18, eq. 2.11 return 0.0; } //______________________________________________________________________________ void kahve() { // For pre-printing a vertical space so that the output is more readable. printf(" ( ) \t\ttracing : %d \n", FUNCTION_TRACING_ON); printf(" ) \t\tMNCOX = %f microA/Vsquare, uA/V^2\n",\ MNCOX); printf(" ( ( \t\tMPCOX = %f microA/Vsquare, uA/V^2\n", MPCOX ); printf(" )) \t\tVTHN = %f volt \n", VTHN); printf(" _..,-(--,.._ \t\tVTHP = %f volt\n", VTHP); printf(" .-;'-.,____,.-'; \t\tGAMMAN = %f sqrt(volt)\n", GAMMAN); printf("(( | | \t\tGAMMAP = %f sqrt(volt)\n", GAMMAP); printf(" ')) ; \t\tLAMBDAN = %f 1/volt\n", LAMBDAN); printf(" '\\ / \t\tLAMBDAP = %f 1/volt\n", LAMBDAP); printf(" .-'`',.____.,'`'-. \t\t_2QF = %f volt\n", _2QF); printf("( '------' )\t\tTOX = %f nm\n", TOX); printf(" '-=..________..--' \t\tCOX = %f fF/microMeterSquare, fF/umV^2\ \n", COX); } //______________________________________________________________________________ int main() { kahve(); separator // page 2 if (false) { printf("<page 2 figure 1> Ids=%f uA\n\n", Ids(1.3, 1, 10, 1)); printf("<page 2 figure 2> Ids=%f uA\n\n", Ids(1.3, 0.3, 10, 2)); printf("<page 2 figure 3> Ids=%f uA\n\n", Ids(0.3, 0.3, 10, 2)); } separator // page 3 if (false) { printf("<page 3 figure 1> Ids=%f uA\n\n", Ids(0.8, 0.8, 10, 0.4, 0.5)); printf("<page 3 figure 2> Ids=%f uA\n\n", Ids(-0.7, -2, 2, 0.4, 0,\ "pmos")); printf("<page 3 figure 3> Ids=%f uA\n\n", Ids(-0.7, -2, 2, 1, -0.5,\ "pmos")); } separator // page 4 if (false) { // Vout could be Vdd/2 = 1.25 V. I will find the Vgate value // that guarantees Id=20uA printf("<page 4 figure 1> Vgate:%f mV, gm_1=%f uMho, gm_2=%f uMho,\ gm_3=%f uMho, Av:%f M\n\n", 1000*Vgs_sat(20, 10, 0.4, 1.25), gm_1(10, 0.4, Vgs_sat(20, 10, 0.4, 1.25), 1.25), gm_2(20, 10, 0.4, 1.25), gm_3(20, Vgs_sat(20, 10, 0.4, 1.25)), -gm_2(20, 10, 0.4, 1.25)*r0(20)/1000000000 ); // Do the same thing for the second figure printf("<page 4 figure 2> Vgate=%f mV, gm_1=%f uMho, gm_2=%f uMho,\ gm_3=%f uMho, Av:%f M\n\n", 1000*Vgs_sat(20, 1, 2, 1.25), gm_1(1, 2, Vgs_sat(20, 1, 2, 1.25), 1.25), gm_2(20, 1, 2, 1.25), gm_3(20, Vgs_sat(20, 1, 2, 1.25)), -gm_2(20, 1, 2, 1.25)*r0(20)/1000000000 ); // For finding the minimum Vds while keeping constant Id=20uA, // I will search for such a // Vds value that makes the Veff equal to Vth of the transistor. // Plot the output below. if (false) { for (double i=1.25 ; i>-0.01 ; i-=0.01) { // i represents the Vds printf("%f\t%f\n", i, Vgs_tri(20, 10, 0.4, i)); } } // When Vds is approximately 89.4mV, Vgs is forced to decrease // below Vth so the transistor becomes off, for the first figure. // For the second one : if (false) { for (double i=2.5 ; i>0.1 ; i-=0.05) { // i represents the Vds printf("%f\t%f\n", i, Vgs_tri(20, 1, 2, i)); } } // When Vds is approximately 632mV, Vgs is forced to // decrease below Vth so the transistor becomes off, // for the second figure on page 4. } separator // page 5 // Since I still want Vout to be 1.25 V, current flowing through // 100K resistor is 12.5uA // 12.5+20=32.5uA, this is the value that I want my transistor to // sink, as a result Vgate must change : if (false) { printf("<page 5 figure 1> Vgate=%f V\n\n", Vgs_sat(32.5, 10, 0.4,\ 1.25)); printf("<page 5 figure 2> Vgate=%f V\n\n", Vgs_sat(32.5, 1, 2, 1.25)); } // page razavi 22 // Just to be sure that the functions are working; // solved Razavi, example 2.2 // Set FUNCTION_TRACING_ON to false and plot the output of this part // (W/L and Vb are arbitrarily choosen as 10/1 and 0.55V respectively) // See "pic/example_2.2_page_22.gif" if (false) { printf("Example 2.2, page 22, Razavi\n\n"); for (double i=0 ; i<2 ; i+=0.1) { // i represents Vds if (i<=0.55) printf("%f\t%f\n", i, gm_triodeRegion(i, 10.0, 1.0)); if (i>0.55) printf("%f\t%f\n", i, gm_1(10, 1, 1, i)); } } separator // page 6 if (false) { // I would like Vout to be 1.25V so the current flowing // through the resistor is 12.5uA // The current the transistor must sink is 20+12.5=32.5uA printf("<page 6 figure 1> Vgate=%f V\n\n", 2.5+Vgs_sat(-32.5, 10, 0.4,\ -1.25, 0, "pmos")); printf("<page 6 figure 2> Vgate=%f V\n\n", 2.5+Vgs_sat(-32.5, 1, 2,\ -1.25, 0, "pmos")); // If Vgate increases, the transistor tends to flow less current // while the ideal current source // tends to keep the current constant. At the end, the negotiation // is done as follows : // Resistor current would decrease, Vout would decrease too. // If Vgate decreases, transistor tends to flow more current while // ideal current source tends to // keep it as it is. In this case resistor current and Vout increase. // In both cases, the small signal which exeeds a certain limit // would be truncated at output to fit into // the "window" defined by Vdd and Vout. // Vgates are approximately found to be 1.769060 V and 0.367007 V // respectively. } separator // page 7 // Arbitrary method 1 // First, find the Rbias, for 10uA I must find the gate, that is, also Vds // { Curly paranthezies are used to isolate the variables declared // within this part from the rest of the code } if (false) { double firstGuess = Vgs_sat(10, 2, 1, 0); // I ignored channel length double selfExistantSecondGuess = Vgs_sat(10, 2, 1, firstGuess); // this must get closer to the real result printf("<page 7 the only figure> Vgs(M3)=%f mV\n", \ 1000*Vgs_sat(10, 2, 1, selfExistantSecondGuess)); // even better // The voltage difference between Rbias is 2.5-0.722010=1.77799V // and I=10uA and V=RI so : printf("<page 7 the only figure> Rbias=%f KOhm\n\n", 1000*(2.5-Vgs_sat(10, 2, 1, selfExistantSecondGuess))/10); // Arbitrary method 2 // I want Vout to be 1.25V so Vgate for M2 is as follows : double vgs, id, rbias; vgs=Vgs_sat(50, 10, 1, 1.25); printf("<page 7 the only figure> Vgs(M2):%f mV\n", vgs*1000); // This is also the Vgate and Vds for M3, let me check if the // current is 10uA for M3 id=Ids(vgs, vgs, 2, 1); printf("<page 7 the only figure> Id(M3):%f uA\n", id); // As seen at the output Id(M3)~9.896754uA, reasonably close to // 10uA. So Rbias is (V=IR) : rbias=(2.5-vgs)/id; printf("<page 7 the only figure> Rbias=%f KOhm\n", rbias*1000); // The two arbitrary methods are close to each other as a verification. // Let us calculate the Vgate for M1 driver transistor : printf("<page 7 the only figure> Vgate(M1):%f V\n", \ 2.5+Vgs_sat(-50, 20, 1, -1.25, 0, "pmos")); // Vgate=1.168V is calculated; coherent with CAD that gives 1.46V // Now the second part : use an NMOS device as the input transistor. // Rbias, Vgate of M2 and M3 do not change since they are isolated // from the input device // But I have to re-calculate the Vgate of M1 which is the input // device (attention : bulk effect) printf("<page 7 second part> Vgate:%f V\n\n",\ 1.25+Vgs_sat(50, 20, 1, 1.25, 1.25)); // Note that Vth=0.792775 (bigger because of bulk effect) and // Vgate=2.2 } separator // page 8 // I want 50uA so the voltage on 4KOhm(20Kohm) resistor is 0.2mV(1V), // Vsb=0.2V and Vds=1.3V, Vs=0.2 // Vgate=Vgs+Vs // For calculating voltage gain I must know gm then call Av_CS_sd() // function so : if (false) { double gm = gm_2(50, 40, 1, 1.3, "nmos"); double Av = Av_CS_sd(gm, 20000, 4000); double Vgate = 0.2+Vgs_sat(50, 40, 1, 1.3, 0.2, "nmos"); printf("<page 8 figure 1> Vgate:%f V Av:%f\n", Vgate, Av); } // For figure 2, Vsb=2.3-2.5=-0.2V, Vout=1V, Vs=2.3V so : // For calculating voltage gain first I must know gm : if (false) { double gm = gm_2(50, 40, 1, -1.3, "pmos"); double Av = Av_CS_sd(gm, 20000, 4000); double Vgate = 2.3+Vgs_sat(-50, 40, 1, -1.3, -0.2, "pmos"); printf("<page 8 figure 2> Vgate:%f V Av:%f\n", Vgate, Av); } // Vsb=0, Vs=2.3V so : // For calculating voltage gain first I must know gm : if (false) { double gm = gm_2(50, 40, 1, -1.3, "pmos"); double Av = Av_CS_sd(gm, 20000, 4000); double Vgate = 2.3+Vgs_sat(-50, 40, 1, -1.3, 0, "pmos"); printf("<page 8 figure 3> Vgate:%f V Av:%f\n\n", Vgate, Av); } separator // page 9 if (false) { // I am going to scan the whole range, I will find the Id value that // requires Vgate to be 1.5 and // in this case I will get the Vth, Vout and V1 values since I will // know the Id value. // Vout=2.5V-RdId, V1=RsId => Vds=Vout-V1=2.5-15000Id if (false) { double Vgate; for (double Id=1.0 ; Id<250.0 ; Id+=1.0) { Vgate=0.005*Id+Vgs_sat(Id, 20, 1, 2.5-0.015*Id,\ 0.005*Id, "nmos"); if (Vgate<1.6 && Vgate>1.4) printf("Id=%f\tVgate=%f\n", Id,\ Vgate); } } // As seen at the out put of the above loop, when Id=120uA, Vgate // becomes ~1.5 and in this case Vth~0.66V // So Vout=1.3V, V1=0.6V. Find the gm : printf("<page 9 the only figure> gm:%f uMho\n\n", gm_3(120, 0.9, 0.6)); } separator // page 10 if (false) { // I must know Id. I will use the same technic // as used in the previous exemple. if (false) { double Vgate; for (double Id=0 ; Id<10000 ; Id+=0.01) { Vgate=Vgs_sat(Id, 20, 1, 2.5-0.01*Id, 0, "nmos"); if (Vgate<1.50001 && Vgate>1.49999) printf("Id=%f\tVgate=%f\n", Id, Vgate); } } // As seen at the out put of the above loop, when Id=1.5mA, // Vgate becomes 1.5 but in this case Vout=-17.5V which is impossible ! // That means transistor is in triod region and the corresponding // formula must be used. if (false) { double Vgate; for (double Id=100 ; Id<500 ; Id+=0.01) { Vgate=Vgs_tri(Id, 20, 1, 2.5-Id/100, 0, "nmos"); if (Vgate<1.51 && Vgate>1.49) printf("Id=%f\tVgate=%f\n", Id, Vgate); } } // Id=243.71 is found from the output of the above loop. // So Vout=2.5-Rd*Id=63mV and the transistor is in triode region so : printf("<page 10 figure 1> gm:%f uMho\n", gm_3(243.71, 1.5)); // yielding gm=487.42uMho and Av=~0 since the transistor is in // triode region, it is attenuating. // For figure 2, I can find Vout. Put Vgs=Vgate-Vs and take Vs out. // Solving that : // Vout=0.8204171436 Volt is found or numericaly I can search for // Vout that makes the Vgate 1.5V : // Remember Vgs=Vgate-Vs => Vgate=Vgs+Vs if (false) { double Vgate; for (double Vout=0 ; Vout<2.5 ; Vout+=0.001) { Vgate=Vout+Vgs_sat(100, 30, 1, 2.5-Vout, Vout, "nmos"); if (Vgate<1.51 && Vgate>1.49) { printf("Vgate=%f\tVout=%f\n", Vgate, Vout); } } } // yielding Vout=650mV } separator // page 11 if (false) { // I must find Rbias for 30 uA : double ids; for (double Rbias=58561 ; Rbias<58562 ; Rbias+=0.01) { ids = Ids(2.5-3*0.00001*Rbias, 2.5-3*0.00001*Rbias, 10, 2, 0,\ "nmos"); if (ids<30.0001 && ids>29.9999) printf("Ids:%f\tRbias:%f\n", \ ids, Rbias); } // yielding 58561.71 Ohm, that is, approximately 58.5 KOhm } separator // page 12 if (false) { // Rbias must be found for 100 uA. if (false) { double ids; for (double Rbias=0 ; Rbias<120000 ; Rbias+=1) { ids = Ids(0.0001*Rbias-2.5, 0.0001*Rbias-2.5, 30, 3, 0, "pmos"); if (ids>-100.1 && ids<-99.9) printf("Ids:%f\tRbias:%f\n", ids, Rbias); } } // yielding 13.745KOhm if (false) { double ids0, ids1, ids2; ids0 = Ids(1.375-2.5, 1.375-2.5, 30, 3, 0, "pmos"); printf("1st, Ids:%f uA\n", ids0); ids1 = Ids(1.375-2.5, -1, 60, 3, 0, "pmos"); printf("2nd, Ids:%f uA\n", ids1); ids2 = Ids(1.375-2.5, -2, 60, 3, 0, "pmos"); printf("3rd, Ids:%f uA\n", ids2); printf("<Page 12> DeltaI:%f uA\n", fabs(ids2-ids1)); } // Yielding : // 1st, Ids:-99.853516 uA // 2nd, Ids:-199.218750 uA // 3rd, Ids:-203.125000 uA // DeltaI=3.906250 uA } separator // page 13 if (false) { // For 40 uA, Vgate must be found for T3 (Vgs=Vgate-Vs => Vgate=Vgs+Vs) double ids; if (false) { for (double Vgs=0.6 ; Vgs<2.5 ; Vgs+=0.00001) { ids = Ids(Vgs, Vgs, 20, 2, 0, "nmos"); if (ids>39.995 && ids<40.005) printf("Ids:%f\tVgate:%f\n", ids, 0.0+Vgs); } // yielding Vgate~698.62mV and then calculate the same thing for // T4 since we know source of T4 now } if (false) { for (double Vgs=0.1 ; Vgs<1.0 ; Vgs+=0.00001) { ids = Ids(Vgs, Vgs, 20, 2, 0.69862, "nmos"); if (ids>39.995 && ids<40.005) printf("Ids:%f\tVgate:%f\n",\ ids, 0.69862+Vgs); } // yielding Vd(T4)~1.58V } // Now we will find the minimum Vd(T2) which can keep T2 in // saturation region double VTH = Vth(0.69862, "nmos"); printf("<page 13 the only figure> Vd(T2):%f V \n\n",\ 0.69862+1.58-0.68962-VTH); // yielding Vth(T2)~907mV } separator // page 14 if (false) { // I want to find the Vgs and also Vds value that makes 40uA // possible for the transistor double ids; if (true) for (double i=1.0 ; i<3.3 ; i+=0.0001) { ids=Ids(i, i, 3.0, 2.0); if (ids<40.01 && ids>39.99) printf("| Vgs:%f\tIds:%f\n", i, ids); } // which yealds Vgs=Vds=1.01V (The gate of T2 is at this potential) // Do the same for T1 if (true) for (double i=0.0 ; i<3.3 ; i+=0.0001) { ids=Ids(i, i, 20.0, 2.0); if (ids<40.1 && ids>39.9) printf(" | Vgs:%f\tIds:%f\n", i, ids); } // which yields 699mV. Do the same thing for T2; I am able to // do this because the bulk effect is ignorable if (true) for (double i=0.0 ; i<3.3 ; i+=0.0001) { ids=Ids(i, i, 20.0, 2.0); if (ids<40.1 && ids>39.9) printf(" | Vgs:%f\tIds:%f\n", i, ids); } // which yields 699mV. Vgs=Vg-Vs => Vs=Vg-Vgs // Vs=1.02-0.699=0.321 So both the transistors operate in // triode region. double r01 = r0(40)/1000000, r02=r0(40)/1000000; double gm1 = gm_triodeRegion(0.321, 20, 2); double gm2 = gm_triodeRegion(0.4, 20, 2, 0.321); printf("<Page 14> r01:%fM r02:%fM gm1:%fuS gm2:%fuS R:%fM\n", r01, r02, gm1, gm2, r01+r02); // yielding gm1=642uS and gm2=800uS. } separator // page 15 if (false) { // First I need the Vgate value of T5 for (double i=-2.5 ; i<2.5 ; i+=0.0001) { double ids=fabs(Ids(i, i, 6.0, 1.0, 0, "pmos")); if (ids<70.1 && ids>69.9) printf("Vgs:%f\tIds:%f\n", i, ids); } // which yields ~-1.175V, Vgate=2.5-1.175=1.325V // For T2, calculate Vgs=-0.8 and Vg=1.7V with exactly the same way. // Then 830mV is found as the value that can keep T3 in saturation // so the correct current mirror functionality could be achieved. } return 0; }